--- hddtemp.orig 2009-10-02 17:00:28.000000000 +0100 +++ hddtemp.c 2009-10-06 11:41:17.000000000 +0100 @@ -54,20 +54,25 @@ #include #include +#include +#include +#include +#include -#ifdef HAVE_NETCAT -#include "helpers.c" -# ifndef NETCAT_PATH -# define NETCAT_PATH "/bin/netcat" +# ifndef HDDTEMP_PORT +# define HDDTEMP_PORT 7634 # endif -# ifndef HDDTEMP_PORT -# define HDDTEMP_PORT "7634" +# ifndef HDDTEMP_BUFFER_SIZE +# define HDDTEMP_BUFFER_SIZE 1023UL +# endif + +# ifndef MAX_DISKS +# define MAX_DISKS 200 # endif # define DOUBLE_DELIMITER "||" # define SINGLE_DELIMITER "|" -#endif #ifdef HAVE_LIBNOTIFY void @@ -177,118 +182,165 @@ #endif } - -#ifdef HAVE_NETCAT -void -read_disks_netcat (t_chip *chip) +/* Function to obtain a socket connected to the hddtemp daemon on the current system. */ +static int get_hddtemp_socket(void) { - char *stdoutput, *stderrput, *cmdline, *tmp, *tmp2, *tmp3; - gboolean result; - gint exit_status = 0; - GError *error = NULL; + const int sock_fd = socket(AF_INET, SOCK_STREAM, 0); /* Create an IPv4 socket. */ - t_chipfeature *cf; + if (sock_fd != -1) + { + struct sockaddr_in ip_addr; - cmdline = g_strdup_printf ("%s localhost %s", NETCAT_PATH, HDDTEMP_PORT); - //g_printf("cmdline=%s\n", cmdline); + /* Build a binary address for port 7634 on localhost. */ + memset(&ip_addr, 0x00, sizeof ip_addr); + ip_addr.sin_family = AF_INET; + ip_addr.sin_port = htons(HDDTEMP_PORT); + inet_pton(AF_INET, "127.0.0.1", &ip_addr.sin_addr); - result = g_spawn_command_line_sync ( (const gchar*) cmdline, - &stdoutput, &stderrput, &exit_status, &error); + /* Now connect to the daemon. */ + if (connect(sock_fd, (struct sockaddr *) &ip_addr, sizeof ip_addr) != 0) + { + gchar * msg_txt; - if (!result) - return; + msg_txt = g_strdup_printf("Connecting socket: %s", g_strerror(errno)); + /* g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, msg_txt); */ + quick_message(msg_txt); + g_free(msg_txt); + close(sock_fd); + return -1; + } + } + else + { + gchar * msg_txt; - //g_printf("stdouput=%s\n", stdoutput); + msg_txt = g_strdup_printf("Opening socket: %s", g_strerror(errno)); + /* g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, msg_txt); */ + quick_message(msg_txt); + g_free(msg_txt); + } - tmp = str_split (stdoutput, DOUBLE_DELIMITER); - do { - //g_printf ("Found token: %s\n", tmp); - cf = g_new(t_chipfeature, 1); + return sock_fd; +} - tmp2 = g_strdup (tmp); - tmp3 = strtok (tmp2, SINGLE_DELIMITER); - cf->devicename = g_strdup(tmp3); - tmp3 = strtok (NULL, SINGLE_DELIMITER); - cf->name = g_strdup(tmp3); +/* This function reads all the bytes of the hddtemp daemon's output, + places them in a dynamically allocated buffer, which should be freed later, + and returns the length of the buffer. */ +static gsize get_hddtemp_text(gchar ** stdoutput) +{ + const int sock_fd = get_hddtemp_socket(); + gsize result_len = 0UL; - g_ptr_array_add(chip->chip_features, cf); - chip->num_features++; + TRACE ("enters get_hddtemp_text"); - g_free (tmp2); - } - while ( (tmp = str_split(NULL, DOUBLE_DELIMITER)) ); + /* Ensure the socket is valid. */ + if (sock_fd != -1) + { + GIOChannel * const sock = g_io_channel_unix_new(sock_fd); /* Use Glib I/O channel for the socket. */ + GIOStatus rc; + GError * err_code = NULL; + gsize bytes_read = 0UL; + gchar input_buffer[HDDTEMP_BUFFER_SIZE+1]; - g_free (stdoutput); -} + do + { + /* Read a buffer of bytes from the socket connected to the hddtemp daemon. */ + rc = g_io_channel_read_chars(sock, input_buffer, HDDTEMP_BUFFER_SIZE, &bytes_read, &err_code); -#else -void -read_disks_fallback (t_chip *chip) -{ - GError *error; - GDir *gdir; - t_chipfeature *chipfeature; - const gchar* dirname; + /* The next piece of code should never be executed, but then ... */ + if (rc == G_IO_STATUS_ERROR) + { + gchar * msg_txt; - TRACE ("enters read_disks_fallback"); + msg_txt = g_strdup_printf("Reading socket: %s (%d)", err_code->message, err_code->code); + /* g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Reading socket: %s (%d)", err_code->message, err_code->code); */ + quick_message(msg_txt); + g_free(msg_txt); + g_error_free(err_code); + err_code = NULL; + break; + } - /* read from /proc/ide */ - error = NULL; - gdir = g_dir_open ("/proc/ide/", 0, &error); + /* If we received some bytes in this read, add it to the output string. */ + if (bytes_read > 0) + { + result_len += bytes_read; + if (*stdoutput == NULL) /* No bytes yet in the string. */ + { + *stdoutput = g_strndup(input_buffer, bytes_read); + } + else + { + gchar * const tmp = *stdoutput; /* Protect the reference to the bytes read previously. */ - while ( (dirname = g_dir_read_name (gdir))!=NULL ) { - if ( strncmp (dirname, "hd", 2)==0 || strncmp (dirname, "sd", 2)==0) { - /* TODO: look, if /dev/dirname exists? */ - chipfeature = g_new0 (t_chipfeature, 1); - chipfeature->devicename = g_strconcat ("/dev/", dirname, NULL); - chipfeature->name = g_strdup(chipfeature->devicename); - g_ptr_array_add (chip->chip_features, chipfeature); - chip->num_features++; + /* Add a trailing NUL to the block of bytes. */ + input_buffer[bytes_read] = '\0'; + /* Concatenate this block to the bytes we have read previously. */ + *stdoutput = g_strconcat(tmp, input_buffer, NULL); + /* Free up the previous bytes. */ + g_free(tmp); + } + } + } while (rc != G_IO_STATUS_EOF); /* Keep looping until the hddtemp daemon closes the socket. */ + + if (g_io_channel_shutdown(sock, TRUE, &err_code) == G_IO_STATUS_ERROR) /* This should always work, but then ... */ + { + gchar * msg_txt; + + msg_txt = g_strdup_printf("Closing socket: %s (%d)", err_code->message, err_code->code); + /* g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, msg_txt); */ + quick_message(msg_txt); + g_free(msg_txt); + g_error_free(err_code); + err_code = NULL; } } - g_dir_close (gdir); - - /* FIXME: read SCSI info from where? SATA? */ + TRACE ("leaves get_hddtemp_text"); - TRACE ("leaves read_disks_fallback"); + /* Return value is the total number of bytes read through the socket. */ + return result_len; } - void -read_disks_linux26 (t_chip *chip) +read_disks(t_chip *chip) { - GDir *gdir; - t_chipfeature *chipfeature; - const gchar* dirname; + gchar * stdoutput = NULL; + t_chipfeature *cf; - TRACE ("enters read_disks_linux26"); + TRACE ("enters read_disks"); - /* read from /sys/block */ - gdir = g_dir_open ("/sys/block/", 0, NULL); - while ( (dirname = g_dir_read_name (gdir))!=NULL ) { - /* if ( strncmp (dirname, "ram", 3)!=0 && - strncmp (dirname, "loop", 4)!=0 && - strncmp (dirname, "md", 2)!=0 && - strncmp (dirname, "fd", 2)!=0 && - strncmp (dirname, "mmc", 3)!=0 && - strncmp (dirname, "dm-", 3)!=0 ) { */ - if ( strncmp (dirname, "hd", 2)==0 || - strncmp (dirname, "sd", 2)==0 ) { - /* TODO: look, if /dev/dirname exists? */ - chipfeature = g_new0 (t_chipfeature, 1); - chipfeature->devicename = g_strconcat ("/dev/", dirname, NULL); /* /proc/ide/hda/model ?? */ - chipfeature->name = g_strdup(chipfeature->devicename); - g_ptr_array_add (chip->chip_features, chipfeature); - chip->num_features++; + get_hddtemp_text(&stdoutput); + + if (stdoutput != NULL) + { + gchar ** string_vec = g_strsplit_set(stdoutput, SINGLE_DELIMITER, MAX_DISKS); + gint i, max_i = 0; + + while (string_vec[max_i] != NULL) + { + ++max_i; } - } - g_dir_close (gdir); + for (i = 1; i < max_i; i += 5) + { + if (strcmp(string_vec[i+2], "NOS") != 0) + { + cf = g_new(t_chipfeature, 1); + cf->devicename = g_strdup(string_vec[i]); + cf->name = g_strdup(string_vec[i]+5); /* Trim off "/dev/". */ - TRACE ("leaves read_disks_linux26"); + g_ptr_array_add(chip->chip_features, cf); + chip->num_features++; + } + } + + g_strfreev(string_vec); + g_free(stdoutput); + } + + TRACE ("leaves read_disks"); } -#endif void remove_unmonitored_drives (t_chip *chip, gboolean *suppressmessage) @@ -368,9 +420,9 @@ int initialize_hddtemp (GPtrArray *chips, gboolean *suppressmessage) { - int generation, major, result, retval; - struct utsname *p_uname; + int retval; t_chip *chip; + static const sensors_chip_name hdd_chip_name = { "Hard disks", 0, 0, "Hard disks" }; g_assert (chips!=NULL); @@ -378,9 +430,8 @@ chip = g_new (t_chip, 1); - chip->chip_name = (const sensors_chip_name *) - ( _("Hard disks"), 0, 0, _("Hard disks") ); - + chip->chip_name = &hdd_chip_name; + chip->chip_features = g_ptr_array_new (); chip->num_features = 0; chip->description = g_strdup(_("S.M.A.R.T. harddisk temperatures")); @@ -388,30 +439,9 @@ chip->sensorId = g_strdup("Hard disks"); chip->type = HDD; - p_uname = (struct utsname *) malloc (sizeof(struct utsname)); - result = uname (p_uname); - if (result!=0) { - g_free(p_uname); - return -1; - } - - generation = atoi ( p_uname->release ); /* this might cause trouble on */ - major = atoi ( p_uname->release+2 ); /* other systems than Linux! */ - /* actually, wanted to use build time configuration therefore */ - - /* Note: This is actually supposed to be carried out by ifdef HAVE_LINUX - and major/minor number stuff from compile time*/ -#ifdef HAVE_NETCAT - read_disks_netcat (chip); -#else - if (strcmp(p_uname->sysname, "Linux")==0 && major>=5) - read_disks_linux26 (chip); - else - read_disks_fallback (chip); /* hopefully, that's a safe variant */ -#endif - - g_free(p_uname); + read_disks(chip); + /* This next call should no longer be necessary. */ remove_unmonitored_drives (chip, suppressmessage); DBG ("numfeatures=%d\n", chip->num_features); if ( chip->num_features>0 ) { /* if (1) */ @@ -433,17 +463,10 @@ double get_hddtemp_value (char* disk, gboolean *suppressmessage) { - gchar *standard_output, *standard_error; - gchar *cmd_line, *msg_text; -#ifndef HAVE_LIBNOTIFY - gchar *checktext; -#endif - gint exit_status=0; - double value; - gboolean result, nevershowagain; - GError *error; - - gchar *tmp, *tmp2, *tmp3; + gchar * standard_output = NULL; + gsize bytes_read; + double value = ZERO_KELVIN; + gboolean nevershowagain; if (suppressmessage!=NULL) nevershowagain = *suppressmessage; @@ -452,143 +475,29 @@ TRACE ("enters get_hddtemp_value for %s with suppress=%d", disk, nevershowagain); /* *suppressmessage); */ -#ifdef HAVE_NETCAT - exit_status = 1; // assume error by default - cmd_line = g_strdup_printf ( "%s localhost %s", NETCAT_PATH, HDDTEMP_PORT); - result = g_spawn_command_line_sync ( (const gchar*) cmd_line, - &standard_output, &standard_error, &exit_status, NULL); - error = g_new(GError, 1); - error->message = g_strdup (_("No concrete error detected.\n")); - if (exit_status==0) + bytes_read = get_hddtemp_text(&standard_output); + if (standard_output != NULL) { - tmp3 = "-255"; - tmp = str_split (standard_output, DOUBLE_DELIMITER); - do { - //g_printf ("Found token: %s for disk %s\n", tmp, disk); - tmp2 = g_strdup (tmp); - tmp3 = strtok (tmp2, SINGLE_DELIMITER); // device name - if (strcmp(tmp3, disk)==0) + gchar ** string_vec = g_strsplit_set(standard_output, SINGLE_DELIMITER, MAX_DISKS); + gint i, max_i = 0; + + while (string_vec[max_i] != NULL) + { + ++max_i; + } + + for (i = 1; i < max_i; i += 5) + { + if (strcmp(string_vec[i], disk) == 0) { - tmp3 = strtok(NULL, SINGLE_DELIMITER); // name - tmp3 = strdup(strtok(NULL, SINGLE_DELIMITER)); // value - // tmp3 = strtok(NULL, SINGLE_DELIMITER); // temperature unit - exit_status = 0; - g_free(error); - error = NULL; - g_free (tmp2); + value = g_strtod(string_vec[i+2], NULL); break; } - g_free (tmp2); } - while ( (tmp = str_split(NULL, DOUBLE_DELIMITER)) ); + g_strfreev(string_vec); g_free(standard_output); - standard_output = tmp3; - } - else - { - error->message = g_strdup (standard_error); - } - -#else - error = NULL; - cmd_line = g_strdup_printf ( "%s -n -q %s", PATH_HDDTEMP, disk); - result = g_spawn_command_line_sync ( (const gchar*) cmd_line, - &standard_output, &standard_error, &exit_status, &error); -#endif - - msg_text = NULL; // wonder if this is still necessary - - DBG ("Exit code %d on %s with stdout of %s.\n", exit_status, disk, standard_output); - - /* filter those with no sensors out */ - if (exit_status==0 && strncmp(disk, "/dev/fd", 6)==0) { /* is returned for floppy disks */ - DBG("exit_status==0 && strncmp(disk, \"/dev/fd\", 6)==0"); - value = 0.0; } - else if ((exit_status==256 || (standard_error && strlen(standard_error)>0)) - && access (PATH_HDDTEMP, X_OK)==0) /* || strlen(standard_error)>0) */ - { - /* note that this check does only work for some versions of hddtemp. */ - if (!nevershowagain) { - msg_text = g_strdup_printf(_("\"hddtemp\" was not executed correctly, " - "although it is executable. This is most probably due " - "to the disks requiring root privileges to read their " - "temperatures, and \"hddtemp\" not being setuid root." - "\n\n" - "An easy but dirty solution is to run \"chmod u+s %s" - "\" as root user and restart this plugin " - "or its panel.\n\n" - "Calling \"%s\" gave the following error:\n%s\nwith a return value of %d.\n"), - PATH_HDDTEMP, cmd_line, standard_error, exit_status); - -#ifdef HAVE_LIBNOTIFY - //msg_text = g_strconcat(msg_text, _("\nYou can disable these notifications in the settings dialog.\n"); - quick_message_notify (msg_text); - nevershowagain = FALSE; -#else - checktext = g_strdup(_("Suppress this message in future")); - nevershowagain = quick_message_with_checkbox(msg_text, checktext); -#endif - - if (suppressmessage!=NULL) - *suppressmessage = nevershowagain; - } - else { - DBG ("Suppressing dialog with exit_code=256 or output on standard_error"); - } - - value = ZERO_KELVIN; - } - /* else if (strlen(standard_error)>0) { - msg_text = g_strdup_printf (_("An error occurred when executing" - " \"%s\":\n%s"), cmd_line, standard_error); - quick_message (msg_text); - value = ZERO_KELVIN; - } */ - - else if (error && (!result || exit_status!=0)) - { - DBG ("error %s\n", error->message); - if (!nevershowagain) { - msg_text = g_strdup_printf (_("An error occurred when executing" - " \"%s\":\n%s"), cmd_line, error->message); -#ifdef HAVE_LIBNOTIFY - quick_message_notify (msg_text); - nevershowagain = FALSE; -#else - checktext = g_strdup(_("Suppress this message in future")); - nevershowagain = quick_message_with_checkbox (msg_text, checktext); -#endif - - if (suppressmessage!=NULL) - *suppressmessage = nevershowagain; - } - else { - DBG ("Suppressing dialog because of error in g_spawn_cl"); - } - value = 0.0; - } - else if (standard_output && strlen(standard_output) > 0) - { - DBG("got the only useful return value of 0 and value of %s.\n", standard_output); - /* hddtemp does not return floating values, but only integer ones. - So have an easier life with atoi. - FIXME: Use strtod() instead?*/ - value = (double) (atoi ( (const char*) standard_output) ); - } - else { - DBG("No condition applied."); - value = 0.0; - } - - g_free (cmd_line); - g_free (standard_output); - g_free (standard_error); - g_free (msg_text); -#ifndef HAVE_LIBNOTIFY - g_free (checktext); -#endif TRACE ("leaves get_hddtemp_value"); @@ -600,7 +509,6 @@ refresh_hddtemp (gpointer chip_feature, gpointer data) { t_chipfeature *cf; - double value; g_assert (chip_feature!=NULL); @@ -608,16 +516,7 @@ cf = (t_chipfeature *) chip_feature; - value = get_hddtemp_value (cf->devicename, NULL); - - /* actually, that's done in the gui part */ - g_free (cf->formatted_value); - /* if (scale == FAHRENHEIT) { - cf->formatted_value = g_strdup_printf(_("%.1f °F"), (float) (value * 9/5 + 32) ); - } else { // Celsius */ - cf->formatted_value = g_strdup_printf(_("%.1f °C"), value); - /* } */ - cf->raw_value = value; + cf->raw_value = get_hddtemp_value (cf->devicename, NULL); TRACE ("leaves refresh_hddtemp"); }