unixsock plugin: Fix a (well hidden) race condition.
[collectd.git] / src / unixsock.c
index 025c91d..38f9025 100644 (file)
@@ -86,8 +86,8 @@ static int us_open_socket (void)
 
        memset (&sa, '\0', sizeof (sa));
        sa.sun_family = AF_UNIX;
-       strncpy (sa.sun_path, (sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
-                       sizeof (sa.sun_path) - 1);
+       sstrncpy (sa.sun_path, (sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
+                       sizeof (sa.sun_path));
        /* unlink (sa.sun_path); */
 
        DEBUG ("unixsock plugin: socket path = %s", sa.sun_path);
@@ -157,41 +157,79 @@ static int us_open_socket (void)
 
 static void *us_handle_client (void *arg)
 {
-       int fd;
+       int fdin;
+       int fdout;
        FILE *fhin, *fhout;
-       char buffer[1024];
-       char *fields[128];
-       int   fields_num;
 
-       fd = *((int *) arg);
+       fdin = *((int *) arg);
        free (arg);
        arg = NULL;
 
-       DEBUG ("Reading from fd #%i", fd);
+       DEBUG ("unixsock plugin: us_handle_client: Reading from fd #%i", fdin);
 
-       fhin  = fdopen (fd, "r");
+       fdout = dup (fdin);
+       if (fdout < 0)
+       {
+               char errbuf[1024];
+               ERROR ("unixsock plugin: dup failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               close (fdin);
+               pthread_exit ((void *) 1);
+       }
+
+       fhin  = fdopen (fdin, "r");
        if (fhin == NULL)
        {
                char errbuf[1024];
                ERROR ("unixsock plugin: fdopen failed: %s",
                                sstrerror (errno, errbuf, sizeof (errbuf)));
-               close (fd);
+               close (fdin);
+               close (fdout);
                pthread_exit ((void *) 1);
        }
 
-       fhout = fdopen (fd, "w");
+       fhout = fdopen (fdout, "w");
        if (fhout == NULL)
        {
                char errbuf[1024];
                ERROR ("unixsock plugin: fdopen failed: %s",
                                sstrerror (errno, errbuf, sizeof (errbuf)));
-               fclose (fhin); /* this closes fd as well */
+               fclose (fhin); /* this closes fdin as well */
+               close (fdout);
+               pthread_exit ((void *) 1);
+       }
+
+       /* change output buffer to line buffered mode */
+       if (setvbuf (fhout, NULL, _IOLBF, 0) != 0)
+       {
+               char errbuf[1024];
+               ERROR ("unixsock plugin: setvbuf failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               fclose (fhin);
+               fclose (fhout);
                pthread_exit ((void *) 1);
        }
 
-       while (fgets (buffer, sizeof (buffer), fhin) != NULL)
+       while (42)
        {
-               int len;
+               char buffer[1024];
+               char buffer_copy[1024];
+               char *fields[128];
+               int   fields_num;
+               int   len;
+
+               errno = 0;
+               if (fgets (buffer, sizeof (buffer), fhin) == NULL)
+               {
+                       if (errno != 0)
+                       {
+                               char errbuf[1024];
+                               WARNING ("unixsock plugin: failed to read from socket #%i: %s",
+                                               fileno (fhin),
+                                               sstrerror (errno, errbuf, sizeof (errbuf)));
+                       }
+                       break;
+               }
 
                len = strlen (buffer);
                while ((len > 0)
@@ -201,45 +239,52 @@ static void *us_handle_client (void *arg)
                if (len == 0)
                        continue;
 
-               DEBUG ("fgets -> buffer = %s; len = %i;", buffer, len);
+               sstrncpy (buffer_copy, buffer, sizeof (buffer_copy));
 
-               fields_num = strsplit (buffer, fields,
+               fields_num = strsplit (buffer_copy, fields,
                                sizeof (fields) / sizeof (fields[0]));
-
                if (fields_num < 1)
                {
-                       close (fd);
-                       break;
+                       fprintf (fhout, "-1 Internal error\n");
+                       fclose (fhin);
+                       fclose (fhout);
+                       pthread_exit ((void *) 1);
                }
 
                if (strcasecmp (fields[0], "getval") == 0)
                {
-                       handle_getval (fhout, fields, fields_num);
+                       handle_getval (fhout, buffer);
                }
                else if (strcasecmp (fields[0], "putval") == 0)
                {
-                       handle_putval (fhout, fields, fields_num);
+                       handle_putval (fhout, buffer);
                }
                else if (strcasecmp (fields[0], "listval") == 0)
                {
-                       handle_listval (fhout, fields, fields_num);
+                       handle_listval (fhout, buffer);
                }
                else if (strcasecmp (fields[0], "putnotif") == 0)
                {
-                       handle_putnotif (fhout, fields, fields_num);
+                       handle_putnotif (fhout, buffer);
                }
                else if (strcasecmp (fields[0], "flush") == 0)
                {
-                       handle_flush (fhout, fields, fields_num);
+                       handle_flush (fhout, buffer);
                }
                else
                {
-                       fprintf (fhout, "-1 Unknown command: %s\n", fields[0]);
-                       fflush (fhout);
+                       if (fprintf (fhout, "-1 Unknown command: %s\n", fields[0]) < 0)
+                       {
+                               char errbuf[1024];
+                               WARNING ("unixsock plugin: failed to write to socket #%i: %s",
+                                               fileno (fhout),
+                                               sstrerror (errno, errbuf, sizeof (errbuf)));
+                               break;
+                       }
                }
        } /* while (fgets) */
 
-       DEBUG ("Exiting..");
+       DEBUG ("unixsock plugin: us_handle_client: Exiting..");
        fclose (fhin);
        fclose (fhout);
 
@@ -247,7 +292,7 @@ static void *us_handle_client (void *arg)
        return ((void *) 0);
 } /* void *us_handle_client */
 
-static void *us_server_thread (void *arg)
+static void *us_server_thread (void __attribute__((unused)) *arg)
 {
        int  status;
        int *remote_fd;
@@ -352,8 +397,15 @@ static int us_config (const char *key, const char *val)
 
 static int us_init (void)
 {
+       static int have_init = 0;
+
        int status;
 
+       /* Initialize only once. */
+       if (have_init != 0)
+               return (0);
+       have_init = 1;
+
        loop = 1;
 
        status = pthread_create (&listen_thread, NULL, us_server_thread, NULL);