(print_sanitized_buffer): Don't care about
[gnupg.git] / jnlib / logging.c
index 913d01b..6a12d87 100644 (file)
 #include <stdio.h>
 #include <string.h>
 #include <stdarg.h>
+#include <stddef.h>
 #include <errno.h>
 #include <time.h>
 #include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 #include <unistd.h>
 #ifdef __MINGW32__
-  #include <io.h>
+#  include <io.h>
 #endif
 
 #define JNLIB_NEED_LOG_LOGV 1
@@ -47,6 +50,7 @@ static char prefix_buffer[80];
 static int with_time;
 static int with_prefix;
 static int with_pid;
+static int force_prefixes;
 
 static int missing_lf;
 static int errorcount;
@@ -84,28 +88,185 @@ log_inc_errorcount (void)
    errorcount++;
 }
 
-void
-log_set_file( const char *name )
+
+/* The follwing 3 functions are used by funopen to write logs to a
+   socket. */
+#if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN)
+struct fun_cookie_s {
+  int fd;
+  int quiet;
+  char name[1];
+};
+
+/* Write NBYTES of BUF to file descriptor FD. */
+static int
+writen (int fd, const unsigned char *buf, size_t nbytes)
+{
+  size_t nleft = nbytes;
+  int nwritten;
+  
+  while (nleft > 0)
+    {
+      nwritten = write (fd, buf, nleft);
+      if (nwritten < 0 && errno == EINTR)
+        continue;
+      if (nwritten < 0)
+        return -1;
+      nleft -= nwritten;
+      buf = buf + nwritten;
+    }
+  
+  return 0;
+}
+
+
+static int 
+fun_writer (void *cookie_arg, const char *buffer, size_t size)
 {
-    FILE *fp = (name && strcmp(name,"-"))? fopen(name, "a") : stderr;
-    if( !fp ) {
-       fprintf(stderr, "failed to open log file `%s': %s\n",
-                                               name, strerror(errno));
-       return;
+  struct fun_cookie_s *cookie = cookie_arg;
+
+  /* Note that we always try to reconnect to the socket but print error
+     messages only the first time an error occured. */
+  if (cookie->fd == -1 )
+    {
+      /* Note yet open or meanwhile closed due to an error. */
+      struct sockaddr_un addr;
+      size_t addrlen;
+
+      cookie->fd = socket (PF_LOCAL, SOCK_STREAM, 0);
+      if (cookie->fd == -1)
+        {
+          if (!cookie->quiet)
+            fprintf (stderr, "failed to create socket for logging: %s\n",
+                     strerror(errno));
+          goto failure;
+        }
+      
+      memset (&addr, 0, sizeof addr);
+      addr.sun_family = PF_LOCAL;
+      strncpy (addr.sun_path, cookie->name, sizeof (addr.sun_path)-1);
+      addr.sun_path[sizeof (addr.sun_path)-1] = 0;
+      addrlen = (offsetof (struct sockaddr_un, sun_path)
+                 + strlen (addr.sun_path) + 1);
+      
+      if (connect (cookie->fd, (struct sockaddr *) &addr, addrlen) == -1)
+        {
+          if (!cookie->quiet)
+            fprintf (stderr, "can't connect to `%s': %s\n",
+                     cookie->name, strerror(errno));
+          close (cookie->fd);
+          cookie->fd = -1;
+          goto failure;
+        }
+      /* Connection established. */
+      cookie->quiet = 0;
+    }
+  
+  if (!writen (cookie->fd, buffer, size))
+    return size; /* Okay. */ 
+
+  fprintf (stderr, "error writing to `%s': %s\n",
+           cookie->name, strerror(errno));
+  close (cookie->fd);
+  cookie->fd = -1;
+
+ failure: 
+  if (!cookie->quiet)
+    {
+      fputs ("switching logging to stderr\n", stderr);
+      cookie->quiet = 1;
     }
-    setvbuf( fp, NULL, _IOLBF, 0 );
 
-    if (logstream && logstream != stderr && logstream != stdout)
-      fclose( logstream );
-    logstream = fp;
-    missing_lf = 0;
+  fwrite (buffer, size, 1, stderr);
+  return size;
 }
 
+static int
+fun_closer (void *cookie_arg)
+{
+  struct fun_cookie_s *cookie = cookie_arg;
+
+  if (cookie->fd != -1)
+    close (cookie->fd);
+  jnlib_free (cookie);
+  return 0;
+}
+#endif /* HAVE_FOPENCOOKIE || HAVE_FUNOPEN */
+
+
+
+
+/* Set the file to write log to.  The sepcial names NULL and "_" may
+   be used to select stderr and names formatted like
+   "socket:///home/foo/mylogs" may be used to write the logging to the
+   socket "/home/foo/mylogs".  If the connection to the socket fails
+   or a write error is detected, the function writes to stderr and
+   tries the next time again to connect the socket.
+  */
 void
-log_set_fd (int fd)
+log_set_file (const char *name) 
 {
   FILE *fp;
+
+  force_prefixes = 0;
+  if (name && !strncmp (name, "socket://", 9) && name[9])
+    {
+#if defined (HAVE_FOPENCOOKIE)||  defined (HAVE_FUNOPEN)
+      struct fun_cookie_s *cookie;
+
+      cookie = jnlib_xmalloc (sizeof *cookie + strlen (name+9));
+      cookie->fd = -1;
+      cookie->quiet = 0;
+      strcpy (cookie->name, name+9);
+
+#ifdef HAVE_FOPENCOOKIE
+      {
+        cookie_io_functions_t io = { NULL };
+        io.write = fun_writer;
+        io.close = fun_closer;
+
+        fp = fopencookie (cookie, "w", io);
+      }
+#else /*!HAVE_FOPENCOOKIE*/
+      {
+        fp = funopen (cookie, NULL, fun_writer, NULL, fun_closer);
+      }
+#endif /*!HAVE_FOPENCOOKIE*/
+#else /* Neither fopencookie nor funopen. */
+      {
+        fprintf (stderr, "system does not support logging to a socket - "
+                 "using stderr\n");
+        fp = stderr;
+      }
+#endif /* Neither fopencookie nor funopen. */
+
+      /* We always need to print the prefix and the pid, so that the
+         server reading the socket can do something meanigful. */
+      force_prefixes = 1;
+    }
+  else
+    fp = (name && strcmp(name,"-"))? fopen (name, "a") : stderr;
+  if (!fp)
+    {
+      fprintf (stderr, "failed to open log file `%s': %s\n",
+               name? name:"[stderr]", strerror(errno));
+      return;
+    }
+  setvbuf (fp, NULL, _IOLBF, 0);
   
+  if (logstream && logstream != stderr && logstream != stdout)
+    fclose (logstream);
+  logstream = fp;
+  missing_lf = 0;
+}
+
+
+void
+log_set_fd (int fd)
+{
+  FILE *fp;
+
+  force_prefixes = 0;
   if (fd == 1)
     fp = stdout;
   else if (fd == 2)
@@ -184,7 +345,7 @@ do_logv( int level, const char *fmt, va_list arg_ptr )
   if (level != JNLIB_LOG_CONT)
     { /* Note this does not work for multiple line logging as we would
        * need to print to a buffer first */
-      if (with_time)
+      if (with_time && !force_prefixes)
         {
           struct tm *tp;
           time_t atime = time (NULL);
@@ -194,14 +355,14 @@ do_logv( int level, const char *fmt, va_list arg_ptr )
                    1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
                    tp->tm_hour, tp->tm_min, tp->tm_sec );
         }
-      if (with_prefix)
+      if (with_prefix || force_prefixes)
         fputs (prefix_buffer, logstream);
-      if (with_pid)
+      if (with_pid || force_prefixes)
         fprintf (logstream, "[%u]", (unsigned int)getpid ());
-      if (!with_time)
+      if (!with_time || force_prefixes)
         putc (':', logstream);
       /* A leading backspace suppresses the extra space so that we can
-         correclty output, programname, filename and linenumber. */
+         correctly output, programname, filename and linenumber. */
       if (fmt && *fmt == '\b')
         fmt++;
       else
@@ -221,6 +382,7 @@ do_logv( int level, const char *fmt, va_list arg_ptr )
     default: fprintf(logstream,"[Unknown log level %d]: ", level ); break;
     }
 
+
   if (fmt)
     {
       vfprintf(logstream,fmt,arg_ptr) ;