-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnetsuspend.cpp
More file actions
1335 lines (1107 loc) · 42.2 KB
/
netsuspend.cpp
File metadata and controls
1335 lines (1107 loc) · 42.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Network-based timed suspend utility
// Leigh Garbs
#include <algorithm>
#include <cmath>
#include <csignal>
#include <cstring>
#include <fstream>
#include <iostream>
#include <linux/if_ether.h>
#include <map>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <vector>
#include "LinuxRawSocketImpl.hpp"
#include "Log.hpp"
#include "ethernet_ii_header.h"
#include "ipv4_header.h"
// Length of the input buffers used during parsing
#define PARSING_BUFFER_LENGTH 1000
enum IdleTimerResetReason
{
NET_IMPORTANT_TRAFFIC,
NET_INTERFACE_BANDWIDTH_THRESHOLD_EXCEEDED,
DISK_BANDWIDTH_THRESHOLD_EXCEEDED,
CPU_USAGE_THRESHOLD_EXCEEDED,
USER_LOGGED_ON,
IDLE_TIMER_EXPIRED,
PROGRAM_START
};
// This struct holds all the data we have to track per-disk
struct Disk
{
unsigned long last_read_time_ms;
bool last_read_time_good;
};
// This struct holds all the data we have to track per network interface
struct Interface
{
unsigned long last_bytes_read;
unsigned long last_bytes_written;
bool last_bytes_good;
};
// Filename of the config file, typically located in /etc/netsuspend
std::string config_filename = "/etc/netsuspend/config";
// Filename of the file containing what interfaces to monitor for bandwidth;
// this file DOES NOT specify the interfaces on which to snoop for important
// traffic
std::string interfaces_filename = "/etc/netsuspend/interfaces";
// Filename of the file containing what disks to monitor
std::string disks_filename = "/etc/netsuspend/disks";
// Filename of the config file, typically located in /etc
std::string ports_filename = "/etc/netsuspend/ports";
// Filename of the log file, typically located in /var/log
std::string log_filename = "/var/log/netsuspend.log";
// Filename of the file in which PID is stored
std::string pid_filename = "/var/run/netsuspend.pid";
// Stores a list of all important ports
std::vector<unsigned short> ports;
// List of interfaces to monitor for bandwidth
std::map<std::string, Interface> interfaces;
// List of disks to monitor for business
std::map<std::string, Disk> disks;
// Populated with the contents of /sys/power/state
std::vector<std::string> supported_sleep_states;
// Index into supported_sleep_states; represents the user's chosen sleep state
int sleep_state_inuse = -1;
// Is host computer big endian?
bool is_big_endian;
// File that logging will go to if it doesn't go to std::out
std::ofstream log_stream;
// Log used to note important events
Log logfile;
// The number of kernel jiffies elapsed
unsigned int last_jiffy_count = 0;
// The number of idle kernel jiffies elapsed
unsigned int last_idle_jiffy_count = 0;
// Name of the interface on which proxying will take place
std::string interface_name = "eth0";
// Whether or not this process should daemonize
bool daemonize = false;
// How long netsuspend should allow the computer to remain idle before putting
// it to sleep
unsigned int idle_timeout = 15;
// Is the system considered active when a user is logged on?
bool user_check_enabled = false;
// Is the system considered active when the CPU is busy?
bool cpu_check_enabled = false;
// Is the system considered active when disks are busy?
bool disk_check_enabled = false;
// Is the system considered active when network interfaces are busy?
bool net_check_enabled = false;
// Amount of time (in seconds) to wait between CPU checks
unsigned int busy_check_period = 10;
// CPU usage percentage threshold, below which the CPU is considered idle
unsigned int cpu_usage_threshold = 5;
// Disk usage percentage threshold, below which the disk is considered idle
unsigned int disk_usage_threshold = 5;
// Network usage threshold, below which the disk is considered idle. Given in
// bits per second
unsigned int net_usage_threshold = 1000000;
// Is verbose logging enabled?
bool verbose_logging_enabled = false;
// Why was the last idle timer reset done?
IdleTimerResetReason last_idle_timer_reset_reason = PROGRAM_START;
//=============================================================================
// Closes the log file; used before log rotation and on shutdown
//=============================================================================
void close_log(int)
{
logfile.write("Closing log file");
log_stream.close();
}
//=============================================================================
// Opens the log file; used after log rotation and during startup
//=============================================================================
void open_log(int)
{
// Try to log to the log file first
log_stream.open(log_filename.c_str(), std::ofstream::app);
// Use the log file if it's good, otherwise just use std::cout
if (log_stream.good())
{
logfile.setOutputStream(log_stream);
}
else
{
logfile.setOutputStream(std::cout);
}
logfile.write("Log file open");
}
//=============================================================================
// Performs any clean up that must be done before the program halts
//=============================================================================
void clean_exit(int)
{
if (logfile.getOutputStream().good())
{
// Log that the service is stopping
logfile.write("Service stopping");
close_log(0);
}
// Delete the PID file
unlink(pid_filename.c_str());
exit(0);
}
//=============================================================================
// Writes the PID of the calling process to file
//=============================================================================
void write_pid_to_file(const std::string& pid_filename)
{
// Get the PID
int pid = getpid();
std::ofstream out_stream(pid_filename.c_str());
out_stream << pid << "\n";
out_stream.close();
}
//=============================================================================
// Processes program arguments
//=============================================================================
bool process_arguments(int argc, char** argv)
{
// Loop over all the arguments, and process them
for (int arg = 1; arg < argc; arg++)
{
// Argument -D daemonizes this process
if (strcmp("-D", argv[arg]) == 0)
{
daemonize = true;
}
// Argument -F sets the program to run in the foreground; do not
// daemonize
else if (strcmp("-F", argv[arg]) == 0)
{
daemonize = false;
}
// Argument --config specifies an alternative config file
else if (strcmp("--config", argv[arg]) == 0 && arg + 1 < argc)
{
arg++;
config_filename = argv[arg];
}
// Argument --ports specifies an alternative ports file
else if (strcmp("--ports", argv[arg]) == 0 && arg + 1 < argc)
{
arg++;
ports_filename = argv[arg];
}
// Argument --disks specifies an alternative disks file
else if (strcmp("--disks", argv[arg]) == 0 && arg + 1 < argc)
{
arg++;
disks_filename = argv[arg];
}
// Argument --interfaces specifies an alternative interfaces file
else if (strcmp("--interfaces", argv[arg]) == 0 && arg + 1 < argc)
{
arg++;
interfaces_filename = argv[arg];
}
// Argument --pidfile specifies an alternative PID file
else if (strcmp("--pidfile", argv[arg]) == 0 && arg + 1 < argc)
{
arg++;
pid_filename = argv[arg];
}
// Argument -i specifies an interface to monitor
else if (strcmp("-i", argv[arg]) == 0 && arg + 1 < argc)
{
arg++;
interface_name = argv[arg];
// 'all' specified for an interface means monitor every interface;
// passing an empty string to the socket bind function later (which
// is what will be done with interface_name) will have this effect
if (interface_name == "all")
{
interface_name = "";
}
}
// Argument --log specifies a file to log to
else if ((strcmp("--log", argv[arg]) == 0 ||
strcmp("-l", argv[arg]) == 0) && arg + 1 < argc)
{
arg++;
log_filename = argv[arg];
}
// Argument --verbose-log means to log more information
else if (strcmp("--verbose-log", argv[arg]) == 0)
{
verbose_logging_enabled = true;
}
}
// If execution reaches here there was an acceptable set of arguments
// provided
return true;
}
//==============================================================================
// Converts binary IP address to a string representation
//==============================================================================
void ip_to_string(const unsigned char* const ip,
std::string& ip_str)
{
char ip_cstr[16];
sprintf(ip_cstr, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
ip_str = ip_cstr;
}
//=============================================================================
// Parses configuration file
//=============================================================================
void process_ports_file(const std::string& filename)
{
// Open the ports file
std::fstream ports_file(filename.c_str());
// Read all the lines out of it
while(!ports_file.eof())
{
// Read a port number
unsigned short port;
ports_file >> port;
// Push the valid port number onto the list if the read was successful
if (ports_file.good())
{
ports.push_back(port);
}
// Clear any error bits
ports_file.clear();
// Discard the rest of the line
char buf = '\0';
while (!ports_file.eof() && buf != '\n')
{
ports_file.get(buf);
}
}
}
//=============================================================================
// Parses config file
//=============================================================================
void process_config_file(const std::string& filename)
{
// Open the config file
std::ifstream config_stream(filename.c_str());
// Initialize some stuff to be used during parsing
char config_line_buffer[PARSING_BUFFER_LENGTH];
std::istringstream convert_to_number;
// Read the entire config file
while(!config_stream.eof())
{
// Read a line of the file
config_stream.getline(config_line_buffer, PARSING_BUFFER_LENGTH);
// Convert it to a string
std::string config_line_string = config_line_buffer;
// Ignore the line if it's a comment
if (config_line_string[0] == '#')
{
continue;
}
// Search through the line for a '='
size_t equal_sign = config_line_string.find('=');
// If there isn't an equal sign, or the equal sign is at the beginning
// or end of the buffer, just go to the next line because this line is
// bad
if (equal_sign == std::string::npos ||
equal_sign == 0 ||
equal_sign == config_line_string.length())
{
continue;
}
// Pull out the strings on the left and right of the equal sign
std::string left_side = config_line_string.substr(0, equal_sign);
std::string right_side = config_line_string.substr(equal_sign + 1,
std::string::npos);
// Clear all convert_to_number flags so it can be used during multiple
// passes through this loop
convert_to_number.clear();
// Now set the appropriate variable based on what was just parsed
if (left_side == "ETH_INTERFACE")
{
interface_name = right_side;
// 'all' specified for an interface means monitor every interface;
// passing an empty string to the socket bind function later (which
// is what will be done with interface_name) will have this effect
if (interface_name == "all")
{
interface_name = "";
}
}
else if (left_side == "LOG_FILE")
{
log_filename = right_side;
}
else if (left_side == "PID_FILE")
{
pid_filename = right_side;
}
else if (left_side == "DAEMONIZE")
{
daemonize = right_side == "yes";
}
else if (left_side == "IDLE_TIMEOUT")
{
convert_to_number.str(right_side);
convert_to_number >> idle_timeout;
}
else if (left_side == "SLEEP_STATE")
{
// Search supported_sleep_states for the named sleep state
for (unsigned int i = 0; i < supported_sleep_states.size(); i++)
{
if (supported_sleep_states[i] == right_side)
{
sleep_state_inuse = i;
break;
}
}
}
else if (left_side == "USER_CHECKING")
{
user_check_enabled = right_side == "enabled";
}
else if (left_side == "CPU_CHECKING")
{
cpu_check_enabled = right_side == "enabled";
}
else if (left_side == "DISK_CHECKING")
{
disk_check_enabled = right_side == "enabled";
}
else if (left_side == "NET_CHECKING")
{
net_check_enabled = right_side == "enabled";
}
else if (left_side == "BUSY_CHECK_PERIOD")
{
convert_to_number.str(right_side);
convert_to_number >> busy_check_period;
}
else if (left_side == "CPU_USAGE_THRESHOLD")
{
convert_to_number.str(right_side);
convert_to_number >> cpu_usage_threshold;
}
else if (left_side == "DISK_USAGE_THRESHOLD")
{
convert_to_number.str(right_side);
convert_to_number >> disk_usage_threshold;
}
else if (left_side == "NET_USAGE_THRESHOLD")
{
convert_to_number.str(right_side);
convert_to_number >> net_usage_threshold;
}
}
}
//=============================================================================
// Parses interfaces file
//=============================================================================
void process_interfaces_file(const std::string& filename)
{
std::ifstream interfaces_stream(filename.c_str());
// Read the entire interfaces file
while(!interfaces_stream.eof())
{
std::string interface;
interfaces_stream >> interface;
// Initialize an Interface structure for this interface
Interface interfaceparams;
interfaceparams.last_bytes_read = 0;
interfaceparams.last_bytes_written = 0;
interfaceparams.last_bytes_good = false;
interfaces[interface] = interfaceparams;
}
}
//=============================================================================
// Parses disks file
//=============================================================================
void process_disks_file(const std::string& filename)
{
std::ifstream disks_stream(filename.c_str());
// Read the entire interfaces file
while(!disks_stream.eof())
{
std::string disk;
disks_stream >> disk;
// Initialize a Disk structure for this disk
Disk diskparams;
diskparams.last_read_time_ms = 0;
diskparams.last_read_time_good = false;
disks[disk] = diskparams;
}
}
//=============================================================================
// Swaps the two bytes beginning at data
//=============================================================================
void byteswap(char* data)
{
// Copy the port's two bytes
char byte1 = *data;
char byte2 = *(data + 1);
// Copy the two bytes back in, in reverse order
memcpy(data, &byte2, 1);
memcpy(data + 1, &byte1, 1);
}
//=============================================================================
// Get and return monotonic time
//=============================================================================
void get_time(timespec& time)
{
clock_gettime(CLOCK_MONOTONIC, &time);
}
//=============================================================================
// Returns a double representation of a timespec timestamp
//=============================================================================
double timespec_to_double(const timespec& time)
{
return time.tv_sec + static_cast<double>(time.tv_nsec) / 1e9;
}
//=============================================================================
// Returns a double representation of a timespec timestamp
//=============================================================================
void double_to_timespec(const double time_sec, timespec& time_ts)
{
double whole_part = std::floor(time_sec);
time_ts.tv_sec = static_cast<unsigned long>(whole_part);
time_ts.tv_nsec = static_cast<unsigned long>((time_sec - whole_part) * 1e9);
}
//=============================================================================
// Handles Ethernet frames as they are sniffed
//=============================================================================
void handle_frame(unsigned char* buffer,
timespec& idle_timer,
char* last_important_ip,
unsigned short& last_important_source_port,
unsigned short& last_important_destination_port)
{
// Assume its an Ethernet II frame
ethernet_ii_header* eth_header = reinterpret_cast<ethernet_ii_header*>(buffer);
// Ethertype for IPv4 packets
char ipv4_type[2];
ipv4_type[0] = 0x08;
ipv4_type[1] = 0x00;
// Ignore any non-IPv4 traffic
if (memcmp(eth_header->ethertype, (void*)ipv4_type, 2) != 0)
{
return;
}
// Get a handy IPv4-style way to reference the packet
ipv4_header* ip_header = reinterpret_cast<ipv4_header*>(buffer + sizeof(ethernet_ii_header));
// Ignore any non-TCP or UDP traffic
if (!(*ip_header->protocol == 0x06 || *ip_header->protocol == 0x11))
{
return;
}
// Figure out how long the header in this IPv4 packet is; we have to do this
// to know where the payload starts, to know where to pick the ports from
// The header length in the packet indicates the number of 32-bit words, so
// the multiply by 4 is necessary to convert to bytes
unsigned short ip_headerlen = (*(ip_header->version_headerlen) & 0x0f) * 4;
// Save a pointer to the start of the IPv4 payload
unsigned char* ip_payload =
buffer + sizeof(ethernet_ii_header) + ip_headerlen;
// Extract the destination port
unsigned short source_port = *(unsigned short*)ip_payload;
// Extract the destination port
unsigned short destination_port = *(unsigned short*)(ip_payload + 2);
// If needed, byteswap the ports
if (!is_big_endian)
{
byteswap((char*)&source_port);
byteswap((char*)&destination_port);
}
// Check both ports against the list of important ports
if (std::find(ports.begin(), ports.end(), source_port) != ports.end() ||
std::find(ports.begin(), ports.end(), destination_port) != ports.end())
{
// Save data on this in case verbose logging is enabled
last_important_source_port = source_port;
last_important_destination_port = destination_port;
}
else
{
// Traffic is not important, leave
return;
}
// Save data on this in case verbose logging is enabled
memcpy(last_important_ip, ip_header->source_ip, 4);
// This is an important packet, so reset the idle timer
get_time(idle_timer);
last_idle_timer_reset_reason = NET_IMPORTANT_TRAFFIC;
// In order to limit how much time this process takes up, sleep here for a
// bit. This helps limit the processing time this process takes when large
// transfers of important traffic are being done.
timespec one_second;
one_second.tv_sec = 1;
one_second.tv_nsec = 0;
nanosleep(&one_second, 0);
}
//=============================================================================
// Updates current with the new current time, as well as idle_timer if a suspend
// happened
//=============================================================================
void update_times(timespec& current_time, timespec& idle_timer)
{
// What is the current time?
timespec new_current_time;
get_time(new_current_time);
// If it been over 5 seconds since the last time the current time was
// checked, assume the computer this process is running on was suspended and
// has resumed. In this case the timer should be reset.
if (timespec_to_double(new_current_time) -
timespec_to_double(current_time) > 5)
{
// Log that this is happening
logfile.write("Suspend detected, resetting timer");
memcpy(&idle_timer, &new_current_time, sizeof(timespec));
}
// Update current time
memcpy(¤t_time, &new_current_time, sizeof(timespec));
}
//=============================================================================
// Determines if any users are logged on
//=============================================================================
bool user_logged_on(bool& logged_on)
{
// Run the who command and get a pipe containing its output
FILE* command_pipe = popen("who | wc -l", "r");
if (command_pipe == NULL)
{
return false;
}
// Get the command output
char buffer[PARSING_BUFFER_LENGTH];
char* fgets_buffer = fgets(buffer, PARSING_BUFFER_LENGTH, command_pipe);
// Close the pipe
if (pclose(command_pipe) == -1 || fgets_buffer == NULL)
{
return false;
}
// Convert the command's output into a number
std::istringstream convert_to_number;
convert_to_number.str(buffer);
unsigned int number_of_users;
convert_to_number >> number_of_users;
logged_on = number_of_users > 0;
return true;
}
//=============================================================================
// Resets the idle timer if a user is logged in
//=============================================================================
void do_user_check(timespec& idle_timer)
{
bool logged_on = false;
bool user_check_success = user_logged_on(logged_on);
// If a user is logged on, reset the idle timer
if (user_check_success && logged_on)
{
get_time(idle_timer);
}
}
//=============================================================================
// Determines if the CPU is busy
//=============================================================================
bool cpu_is_busy()
{
// Open /proc/stat, this is where the CPU stats are
std::ifstream is("/proc/stat", std::ofstream::in);
// All the aggregate CPU stats are on the first line, the rest will be
// ignored
// Read and discard the 'cpu' at the beginning
std::string not_used;
is >> not_used;
// Will be filled in by the loop below
unsigned int jiffy_count = 0;
unsigned int idle_jiffy_count = 0;
// Get the first three jiffy counts
unsigned int temp;
for (int i = 0; i < 3; i++)
{
is >> temp;
jiffy_count += temp;
}
// Read the idle jiffy count
is >> idle_jiffy_count;
jiffy_count += idle_jiffy_count;
// Get the rest of the jiffy counts
for (int i = 0; i < 6; i++)
{
is >> temp;
jiffy_count += temp;
}
// Close /proc/stat
is.close();
// Now compare the current jiffy counts with the last recorded jiffy counts
// to determine idleness
unsigned int idle_jiffies_elapsed = idle_jiffy_count - last_idle_jiffy_count;
unsigned int jiffies_elapsed = jiffy_count - last_jiffy_count;
// Compute the usage percentage
double usage_pct =
(1 - ((double)idle_jiffies_elapsed / (double)jiffies_elapsed)) * 100;
// Save the jiffy counts that were just calculated
last_idle_jiffy_count = idle_jiffy_count;
last_jiffy_count = jiffy_count;
return usage_pct > cpu_usage_threshold;
}
//=============================================================================
// Resets the idle timer if the CPU is busy
//=============================================================================
void do_cpu_check(timespec& idle_timer)
{
// If the CPU is busy, reset the timer
if (cpu_is_busy())
{
get_time(idle_timer);
last_idle_timer_reset_reason = CPU_USAGE_THRESHOLD_EXCEEDED;
}
}
//=============================================================================
// Determines if the disks are busy
//=============================================================================
bool disk_is_busy()
{
// Open /proc/diskstats, this is the file we're going to check
std::ifstream diskstats_stream("/proc/diskstats");
// Initialize some stuff to be used during parsing
char diskstats_line_buffer[PARSING_BUFFER_LENGTH];
// Will be set true if a disk is busy
bool is_busy = false;
// Read the entire config file
while(!diskstats_stream.eof())
{
// Read a line of the file
diskstats_stream.getline(diskstats_line_buffer, PARSING_BUFFER_LENGTH);
// Does this line correspond to any of the disks we're supposed to be
// monitoring?
// First thing to do is compare the disk on this line to all the disks
// we're supposed to monitor
std::istringstream diskstats_line_stream(diskstats_line_buffer);
std::string disk;
diskstats_line_stream >> disk >> disk >> disk;
// If disk is empty then we're probably reading the last empty line. At
// any rate don't consider this line
if (disk.empty())
{
continue;
}
for (std::map<std::string, Disk>::iterator i = disks.begin();
i != disks.end();
++i)
{
// See if this disk is one we're supposed to monitor
if (i->first == disk)
{
// Read out the 10th field
std::string read_time_ms_str;
for (unsigned int j = 0; j < 10; j++)
{
diskstats_line_stream >> read_time_ms_str;
}
std::istringstream convert_to_number(read_time_ms_str);
unsigned long read_time_ms;
convert_to_number >> read_time_ms;
// Only check disk usage time if the counter hasn't overflowed
if (read_time_ms >= i->second.last_read_time_ms)
{
// What's the time disk usage pct?
double usage_pct =
static_cast<double>(read_time_ms -
i->second.last_read_time_ms) /
1000.0 /
static_cast<double>(busy_check_period) *
100.0;
// Is this disk busy?
if (i->second.last_read_time_good && usage_pct >
disk_usage_threshold)
{
is_busy = true;
}
}
// Set things up to work during the next check
i->second.last_read_time_ms = read_time_ms;
i->second.last_read_time_good = true;
}
}
}
return is_busy;
}
//=============================================================================
// Resets the idle timer if the disks are busy
//=============================================================================
void do_disk_check(timespec& idle_timer)
{
// If the disks are busy, reset the timer
if (disk_is_busy())
{
get_time(idle_timer);
last_idle_timer_reset_reason = DISK_BANDWIDTH_THRESHOLD_EXCEEDED;
}
}
//=============================================================================
// Determines if the network is busy
//=============================================================================
bool net_is_busy()
{
// Open /proc/net/dev, this is the file we're going to check
std::ifstream netstats_stream("/proc/net/dev");
// Initialize some stuff to be used during parsing
char netstats_line_buffer[PARSING_BUFFER_LENGTH];
// Will be set true if a network interface is busy
bool is_busy = false;
// The first two lines of this file aren't useful so read and discard them
netstats_stream.getline(netstats_line_buffer, PARSING_BUFFER_LENGTH);
netstats_stream.getline(netstats_line_buffer, PARSING_BUFFER_LENGTH);
// Read the entire file
while(!netstats_stream.eof())
{
// Read a line of the file
netstats_stream.getline(netstats_line_buffer, PARSING_BUFFER_LENGTH);
// Does this line correspond to any of the interfaces we're supposed to
// be monitoring?
// First thing to do is compare the interface on this line to all the
// disks we're supposed to monitor
std::istringstream netstats_line_stream(netstats_line_buffer);
std::string interface;
netstats_line_stream >> interface;
if (interface.empty())
{
continue;
}
// Get rid of the colon on the end
interface = interface.substr(0, interface.length() - 1);
// Does this interface match any of the interfaces we're supposed to be
// watching for?
std::map<std::string, Interface>::iterator i = interfaces.find(interface);
if (i != interfaces.end())
{
unsigned long bytes_read = 0;
unsigned long bytes_written= 0;
netstats_line_stream >> bytes_read;
// Discard the next 7 fields and keep the 8th, that's the # of bytes
// written
for (unsigned int j = 0; j < 8; j++)
{
netstats_line_stream >> bytes_written;
}
// Come up with the average number of bytes written per second over
// the last "busy_check_period" seconds
double avg_bytes_read =
static_cast<double>(bytes_read - i->second.last_bytes_read) /
static_cast<double>(busy_check_period);
double avg_bytes_written =
static_cast<double>(bytes_written - i->second.last_bytes_written) /
static_cast<double>(busy_check_period);
// If we've read or written more than the threshold and we have a
// good last value to base this calculation on, then the network is
// busy
if (i->second.last_bytes_good &&
(avg_bytes_read > net_usage_threshold ||
avg_bytes_written > net_usage_threshold))
{
is_busy = true;
}
// Save state for the next iteration
i->second.last_bytes_read = bytes_read;
i->second.last_bytes_written = bytes_written;
i->second.last_bytes_good = true;
}
}
return is_busy;
}
//=============================================================================
// Resets the idle timer if the network is busy
//=============================================================================
void do_net_check(timespec& idle_timer)
{
// If the network is busy, reset the timer
if (net_is_busy())
{
get_time(idle_timer);
last_idle_timer_reset_reason = NET_INTERFACE_BANDWIDTH_THRESHOLD_EXCEEDED;
}
}
//=============================================================================
// Reads /sys/power/state and stores each word to supported_sleep_states
//=============================================================================
bool discover_supported_sleep_states()
{
// Get rid of whatever is already in there
supported_sleep_states.clear();
// Try to open the file then check if it's really open
std::ifstream sys_power_state("/sys/power/state");
if (!sys_power_state.is_open())