2
/* Copyright 2004-2010 Fabian Nowak (timystery@arcor.de)
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or
7
* (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
/* Note for programmers and editors: Try to use 4 spaces instead of Tab! */
25
/* Package includes */
27
#include <middlelayer.h>
29
#include <sensors-interface-common.h>
31
/* Gtk/Glib includes */
33
/* #include <glib/garray.h>
34
#include <glib/gdir.h>
35
#include <glib/gerror.h>
36
#include <glib/gmem.h>
37
#include <glib/gmessages.h>
38
#include <glib/gprintf.h>
39
#include <glib/gspawn.h>
40
#include <glib/gstrfuncs.h> */
42
/* #include <gtk/gtkcheckbutton.h>
43
#include <gtk/gtkmessagedialog.h>
44
#include <gtk/gtklabel.h>
45
#include <gtk/gtkstock.h> */
48
#if defined(HAVE_LIBNOTIFY4) || defined(HAVE_LIBNOTIFY7)
49
#include <libnotify/notify.h>
51
/* #include <stdio.h> */
55
#include <sys/types.h>
56
#include <sys/socket.h>
57
#include <netinet/in.h>
60
#include <sys/utsname.h>
67
# define NETCAT_PATH "/bin/netcat"
69
# define DOUBLE_DELIMITER "||"
70
# define SINGLE_DELIMITER "|"
74
# define HDDTEMP_PORT 7634
78
#define REPLY_MAX_SIZE 512
81
/* forward declaration for GCC 4.3 -Wall */
82
#if defined(HAVE_LIBNOTIFY4) || defined(HAVE_LIBNOTIFY7)
83
void notification_suppress_messages (NotifyNotification *n, gchar *action, gpointer *data);
86
void quick_message_notify (gchar *message);
87
void quick_message (gchar *message);
88
void read_disks_netcat (t_chip *chip);
89
void read_disks_linux26 (t_chip *chip);
90
int get_hddtemp_d_str (char *buffer, size_t bufsize);
91
void read_disks_fallback (t_chip *chip);
92
void remove_unmonitored_drives (t_chip *chip, gboolean *suppressmessage);
93
void populate_detected_drives (t_chip *chip);
95
#if defined(HAVE_LIBNOTIFY4) || defined(HAVE_LIBNOTIFY7)
97
notification_suppress_messages (NotifyNotification *n, gchar *action, gpointer *data)
99
if (strcmp(action, "confirmed")!=0)
102
/* FIXME: Use channels or propagate private object or use static global variable */
105
void quick_message_notify (gchar *message)
107
NotifyNotification *nn;
108
gchar *summary, *body, *icon;
109
GError *error = NULL;
111
summary = "Hddtemp Information";
113
icon = "xfce-sensors";
115
if (!notify_is_initted())
116
notify_init(PACKAGE); /* NOTIFY_APPNAME */
118
#ifdef HAVE_LIBNOTIFY7
119
nn = notify_notification_new (summary, body, icon);
120
#elif HAVE_LIBNOTIFY4
121
nn = notify_notification_new (summary, body, icon, NULL);
123
/* FIXME: Use channels or propagate private object or use static global variable */
124
//notify_notification_add_action (nn,
126
//_("Don't show this message again"),
127
//(NotifyActionCallback) notification_suppress_messages,
129
notify_notification_show(nn, &error);
132
void quick_message_dialog (gchar *message)
135
GtkWidget *dialog; /*, *label; */
137
TRACE ("enters quick_message");
139
dialog = gtk_message_dialog_new (NULL,
140
GTK_DIALOG_DESTROY_WITH_PARENT,
145
/* dialog = gtk_dialog_new_with_buttons (_("Could not run \"hddtemp\""),
146
NULL, 0, // GTK DIALOG NO MODAL ;-)
147
GTK_STOCK_CLOSE, GTK_RESPONSE_NONE,
149
label = gtk_label_new (message);
150
gtk_label_set_line_wrap (GTK_LABEL(label), TRUE);
151
gtk_label_set_width_chars (GTK_LABEL(label), 60); */
153
g_signal_connect_swapped (dialog, "response",
154
G_CALLBACK (gtk_widget_destroy), dialog);
157
gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), label);
158
gtk_widget_show_all (dialog); */
159
gtk_dialog_run(GTK_DIALOG(dialog));
161
TRACE ("leaves quick_message");
164
gboolean quick_message_with_checkbox (gchar *message, gchar *checkboxtext) {
166
GtkWidget *dialog, *checkbox; /*, *label; */
169
TRACE ("enters quick_message");
171
dialog = gtk_message_dialog_new (NULL,
172
0, /* GTK_DIALOG_DESTROY_WITH_PARENT */
177
gtk_window_set_title(GTK_WINDOW(dialog), _("Sensors Plugin"));
179
checkbox = gtk_check_button_new_with_mnemonic (checkboxtext);
181
gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), checkbox, FALSE, FALSE, 0);
182
gtk_widget_show(checkbox);
184
gtk_dialog_run(GTK_DIALOG(dialog));
186
is_active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(checkbox));
188
gtk_widget_destroy (dialog);
190
TRACE ("leaves quick_message");
197
void quick_message (gchar *message)
199
#if defined(HAVE_LIBNOTIFY4) || defined(HAVE_LIBNOTIFY7)
200
quick_message_notify (message);
202
quick_message_dialog (message);
210
read_disks_netcat (t_chip *chip)
212
char reply[REPLY_MAX_SIZE], *tmp, *tmp2, *tmp3;
217
result = get_hddtemp_d_str(reply, REPLY_MAX_SIZE);
218
DBG ("reply=%s\n", reply);
220
tmp = str_split (reply, DOUBLE_DELIMITER);
222
//g_printf ("Found token: %s\n", tmp);
223
cf = g_new0(t_chipfeature, 1);
225
tmp2 = g_strdup (tmp);
226
tmp3 = strtok (tmp2, SINGLE_DELIMITER);
227
cf->devicename = g_strdup(tmp3);
228
tmp3 = strtok (NULL, SINGLE_DELIMITER);
229
cf->name = g_strdup(tmp3);
231
g_ptr_array_add(chip->chip_features, cf);
232
chip->num_features++;
236
while ( (tmp = str_split(NULL, DOUBLE_DELIMITER)) );
238
// g_free (stdoutput);
243
read_disks_fallback (t_chip *chip)
247
t_chipfeature *chipfeature;
248
const gchar* dirname;
250
TRACE ("enters read_disks_fallback");
252
/* read from /proc/ide */
254
gdir = g_dir_open ("/proc/ide/", 0, &error);
256
while ( (dirname = g_dir_read_name (gdir))!=NULL ) {
257
if ( strncmp (dirname, "hd", 2)==0 || strncmp (dirname, "sd", 2)==0) {
258
/* TODO: look, if /dev/dirname exists? */
259
chipfeature = g_new0 (t_chipfeature, 1);
260
chipfeature->devicename = g_strconcat ("/dev/", dirname, NULL);
261
chipfeature->name = g_strdup(chipfeature->devicename);
262
g_ptr_array_add (chip->chip_features, chipfeature);
263
chip->num_features++;
269
/* FIXME: read SCSI info from where? SATA? */
271
TRACE ("leaves read_disks_fallback");
276
read_disks_linux26 (t_chip *chip)
279
t_chipfeature *chipfeature;
280
const gchar* dirname;
282
TRACE ("enters read_disks_linux26");
284
/* read from /sys/block */
285
gdir = g_dir_open ("/sys/block/", 0, NULL);
286
while ( (dirname = g_dir_read_name (gdir))!=NULL ) {
287
/* if ( strncmp (dirname, "ram", 3)!=0 &&
288
strncmp (dirname, "loop", 4)!=0 &&
289
strncmp (dirname, "md", 2)!=0 &&
290
strncmp (dirname, "fd", 2)!=0 &&
291
strncmp (dirname, "mmc", 3)!=0 &&
292
strncmp (dirname, "dm-", 3)!=0 ) { */
293
if ( strncmp (dirname, "hd", 2)==0 ||
294
strncmp (dirname, "sd", 2)==0 ) {
295
/* TODO: look, if /dev/dirname exists? */
296
chipfeature = g_new0 (t_chipfeature, 1);
297
chipfeature->devicename = g_strconcat ("/dev/", dirname, NULL); /* /proc/ide/hda/model ?? */
298
chipfeature->name = g_strdup(chipfeature->devicename);
299
g_ptr_array_add (chip->chip_features, chipfeature);
300
chip->num_features++;
306
TRACE ("leaves read_disks_linux26");
311
remove_unmonitored_drives (t_chip *chip, gboolean *suppressmessage)
314
t_chipfeature *chipfeature;
316
TRACE ("enters remove_unmonitored_drives");
318
for (i=0; i<chip->num_features; i++)
320
chipfeature = g_ptr_array_index (chip->chip_features, i);
321
result = get_hddtemp_value (chipfeature->devicename, suppressmessage);
324
DBG ("removing single disk");
325
free_chipfeature ( (gpointer) chipfeature, NULL);
326
g_ptr_array_remove_index (chip->chip_features, i);
328
chip->num_features--;
330
else if (result == ZERO_KELVIN)
332
for (i=0; i < chip->num_features; i++) {
333
DBG ("remove %d\n", i);
334
chipfeature = g_ptr_array_index (chip->chip_features, i);
335
free_chipfeature ( (gpointer) chipfeature, NULL);
337
g_ptr_array_free (chip->chip_features, TRUE);
338
// chip->chip_features = g_ptr_array_new();
339
chip->num_features=0;
340
DBG ("Returning because of bad hddtemp.\n");
345
TRACE ("leaves remove_unmonitored_drives");
350
populate_detected_drives (t_chip *chip)
354
t_chipfeature *chipfeature;
356
TRACE ("enters populate_detected_drives");
358
chip->sensorId = g_strdup(_("Hard disks"));
360
for (diskIndex=0; diskIndex < chip->num_features; diskIndex++)
362
chipfeature = g_ptr_array_index (chip->chip_features, diskIndex);
363
g_assert (chipfeature!=NULL);
365
chipfeature->address = diskIndex;
367
/* chipfeature->name = g_strdup(chipfeature->devicename); */
369
chipfeature->color = g_strdup("#B000B0");
370
chipfeature->valid = TRUE;
371
chipfeature->formatted_value = g_strdup ("0.0"); /* _printf("%+5.1f", 0.0); */
372
chipfeature->raw_value = 0.0;
374
chipfeature->class = TEMPERATURE;
375
chipfeature->min_value = 10.0;
376
chipfeature->max_value = 50.0;
378
chipfeature->show = FALSE;
381
TRACE ("leaves populate_detected_drives");
386
initialize_hddtemp (GPtrArray *chips, gboolean *suppressmessage)
388
int generation, major, result, retval;
389
struct utsname *p_uname;
392
g_assert (chips!=NULL);
394
TRACE ("enters initialize_hddtemp");
396
chip = g_new0 (t_chip, 1);
398
/* chip->chip_name = (const sensors_chip_name *)
399
( _("Hard disks"), 0, 0, _("Hard disks") ); */
401
chip->chip_features = g_ptr_array_new ();
402
chip->num_features = 0;
403
chip->description = g_strdup(_("S.M.A.R.T. harddisk temperatures"));
404
chip->name = g_strdup(_("Hard disks"));
405
chip->sensorId = g_strdup("Hard disks");
408
p_uname = (struct utsname *) malloc (sizeof(struct utsname));
409
result = uname (p_uname);
415
generation = atoi ( p_uname->release ); /* this might cause trouble on */
416
major = atoi ( p_uname->release+2 ); /* other systems than Linux! */
417
/* actually, wanted to use build time configuration therefore */
419
/* Note: This is actually supposed to be carried out by ifdef HAVE_LINUX
420
and major/minor number stuff from compile time*/
422
read_disks_netcat (chip);
424
if (strcmp(p_uname->sysname, "Linux")==0 && major>=5)
425
read_disks_linux26 (chip);
427
read_disks_fallback (chip); /* hopefully, that's a safe variant */
432
remove_unmonitored_drives (chip, suppressmessage);
433
DBG ("numfeatures=%d\n", chip->num_features);
434
if ( chip->num_features>0 ) { /* if (1) */
436
populate_detected_drives (chip);
437
g_ptr_array_add (chips, chip);
444
TRACE ("leaves initialize_hddtemp");
452
get_hddtemp_d_str (char *buffer, size_t bufsize)
455
struct sockaddr_in servername;
456
struct hostent *hostinfo;
457
int nbytes = 0, nchunk = 0;
459
/* Create the socket. */
460
sock = socket(PF_INET, SOCK_STREAM, 0);
465
/* Connect to the server. */
466
servername.sin_family = AF_INET;
467
servername.sin_port = htons(HDDTEMP_PORT);
468
hostinfo = gethostbyname("localhost");
469
if (hostinfo == NULL) {
470
/* fprintf (stderr, "Unknown host %s.\n", hostname);*/
473
servername.sin_addr = *(struct in_addr *) hostinfo->h_addr;
475
if (connect (sock, (struct sockaddr *) &servername, sizeof (servername)) < 0) {
476
/* perror ("connect (client)");*/
480
/* Read data from server. */
482
nchunk = read(sock, buffer+nbytes, bufsize-nbytes-1);
485
/* perror ("read");*/
488
} else if (nchunk == 0) {
504
get_hddtemp_value (char* disk, gboolean *suppressmessage)
506
gchar *standard_output=NULL, *standard_error=NULL;
507
gchar *cmd_line=NULL, *msg_text=NULL;
509
#if !defined(HAVE_LIBNOTIFY4) && !defined(HAVE_LIBNOTIFY7)
510
gchar *checktext = NULL;
514
gboolean result=FALSE, nevershowagain;
518
gchar *tmp, *tmp2, *tmp3;
519
char reply[REPLY_MAX_SIZE];
523
if (suppressmessage!=NULL)
524
nevershowagain = *suppressmessage;
526
nevershowagain = FALSE;
528
TRACE ("enters get_hddtemp_value for %s with suppress=%d", disk, nevershowagain); /* *suppressmessage); */
531
/* exit_status = 1; // assume error by default
532
cmd_line = g_strdup_printf ( "%s localhost %s", NETCAT_PATH, HDDTEMP_PORT);
533
result = g_spawn_command_line_sync ( (const gchar*) cmd_line,
534
&standard_output, &standard_error, &exit_status, NULL);
535
error = g_new(GError, 1);
536
error->message = g_strdup (_("No concrete error detected.\n"));
539
read_size = get_hddtemp_d_str(reply, REPLY_MAX_SIZE);
543
tmp = str_split (reply, DOUBLE_DELIMITER);
545
//g_printf ("Found token: %s for disk %s\n", tmp, disk);
546
tmp2 = g_strdup (tmp);
547
tmp3 = strtok (tmp2, SINGLE_DELIMITER); // device name
548
if (strcmp(tmp3, disk)==0)
550
tmp3 = strtok(NULL, SINGLE_DELIMITER); // name
551
tmp3 = strdup(strtok(NULL, SINGLE_DELIMITER)); // value
552
// tmp3 = strtok(NULL, SINGLE_DELIMITER); // temperature unit
561
while ( (tmp = str_split(NULL, DOUBLE_DELIMITER)) );
563
/* g_free(standard_output);*/
564
standard_output = tmp3;
569
error->message = g_strdup (standard_error);
574
cmd_line = g_strdup_printf ( "%s -n -q %s", PATH_HDDTEMP, disk);
575
result = g_spawn_command_line_sync ( (const gchar*) cmd_line,
576
&standard_output, &standard_error, &exit_status, &error);
579
msg_text = NULL; // wonder if this is still necessary
581
DBG ("Exit code %d on %s with stdout of %s.\n", exit_status, disk, standard_output);
583
/* filter those with no sensors out */
584
if (exit_status==0 && strncmp(disk, "/dev/fd", 6)==0) { /* is returned for floppy disks */
585
DBG("exit_status==0 && strncmp(disk, \"/dev/fd\", 6)==0");
588
else if ((exit_status==256 || (standard_error && strlen(standard_error)>0))
589
&& access (PATH_HDDTEMP, X_OK)==0) /* || strlen(standard_error)>0) */
591
/* note that this check does only work for some versions of hddtemp. */
592
if (!nevershowagain) {
593
msg_text = g_strdup_printf(_("\"hddtemp\" was not executed correctly, "
594
"although it is executable. This is most probably due "
595
"to the disks requiring root privileges to read their "
596
"temperatures, and \"hddtemp\" not being setuid root."
598
"An easy but dirty solution is to run \"chmod u+s %s"
599
"\" as root user and restart this plugin "
601
"Calling \"%s\" gave the following error:\n%s\nwith a return value of %d.\n"),
602
PATH_HDDTEMP, cmd_line, standard_error, exit_status);
604
#if defined(HAVE_LIBNOTIFY4) || defined(HAVE_LIBNOTIFY7)
605
//msg_text = g_strconcat(msg_text, _("\nYou can disable these notifications in the settings dialog.\n");
606
quick_message_notify (msg_text);
607
nevershowagain = FALSE;
609
checktext = g_strdup(_("Suppress this message in future"));
610
nevershowagain = quick_message_with_checkbox(msg_text, checktext);
613
if (suppressmessage!=NULL)
614
*suppressmessage = nevershowagain;
617
DBG ("Suppressing dialog with exit_code=256 or output on standard_error");
622
/* else if (strlen(standard_error)>0) {
623
msg_text = g_strdup_printf (_("An error occurred when executing"
624
" \"%s\":\n%s"), cmd_line, standard_error);
625
quick_message (msg_text);
629
else if (error && (!result || exit_status!=0))
631
DBG ("error %s\n", error->message);
632
if (!nevershowagain) {
633
msg_text = g_strdup_printf (_("An error occurred when executing"
634
" \"%s\":\n%s"), cmd_line, error->message);
635
#if defined(HAVE_LIBNOTIFY4) || defined(HAVE_LIBNOTIFY7)
636
quick_message_notify (msg_text);
637
nevershowagain = FALSE;
639
checktext = g_strdup(_("Suppress this message in future"));
640
nevershowagain = quick_message_with_checkbox (msg_text, checktext);
643
if (suppressmessage!=NULL)
644
*suppressmessage = nevershowagain;
647
DBG ("Suppressing dialog because of error in g_spawn_cl");
651
else if (standard_output && strlen(standard_output) > 0)
653
DBG("got the only useful return value of 0 and value of %s.\n", standard_output);
654
/* hddtemp does not return floating values, but only integer ones.
655
So have an easier life with atoi.
656
FIXME: Use strtod() instead?*/
657
value = (double) (atoi ( (const char*) standard_output) );
660
DBG("No condition applied.");
665
g_free (standard_output);
666
g_free (standard_error);
668
#if !defined(HAVE_LIBNOTIFY4) && !defined(HAVE_LIBNOTIFY7)
675
TRACE ("leaves get_hddtemp_value");
682
refresh_hddtemp (gpointer chip_feature, gpointer data)
687
gboolean *suppress = NULL;
689
g_assert (chip_feature!=NULL);
691
TRACE ("enters refresh_hddtemp");
695
sensors = (t_sensors *) data;
696
suppress = &(sensors->suppressmessage);
699
cf = (t_chipfeature *) chip_feature;
701
value = get_hddtemp_value (cf->devicename, suppress);
703
/* actually, that's done in the gui part */
704
g_free (cf->formatted_value);
705
/* if (scale == FAHRENHEIT) {
706
cf->formatted_value = g_strdup_printf(_("%.1f °F"), (float) (value * 9/5 + 32) );
707
} else { // Celsius */
708
cf->formatted_value = g_strdup_printf(_("%.1f °C"), value);
710
cf->raw_value = value;
712
TRACE ("leaves refresh_hddtemp");