Index: trunk/src/yulectl.c
===================================================================
--- trunk/src/yulectl.c	(revision 454)
+++ trunk/src/yulectl.c	(revision 481)
@@ -24,4 +24,5 @@
 #include <stdlib.h>
 #include <string.h> 
+#include <ctype.h>
 #include <errno.h>
 
@@ -41,9 +42,12 @@
 #define SH_MAXMSG 209
 
+#if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED) && \
+  !defined(HAVE_STRUCT_CMSGCRED) && !defined(HAVE_STRUCT_FCRED) && \
+  !(defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
+#define SH_REQ_PASSWORD 1
+#endif
+
 static int    sock     = -1;
-static char * sockname = NULL;
-
 static char   password[15] = "";
-
 static int    verbose = 0;
 
@@ -86,15 +90,8 @@
 
 
-int 
-make_named_socket (char * sockname)
+static int 
+create_unix_socket ()
 {
   int sock;
-
-#if 0
-  struct sockaddr_un name;
-  size_t size;
-#else
-  (void) sockname;
-#endif
 
   /* Create the socket. */
@@ -107,27 +104,8 @@
     }
 
-#if 0
-  /* Bind a name to the socket. */
-  name.sun_family = AF_FILE;
-  strcpy (name.sun_path, sockname);
-
-  /* The size of the address is
-     the offset of the start of the filename,
-     plus its length,
-     plus one for the terminating null byte. */
-  size = (offsetof (struct sockaddr_un, sun_path)
-          + strlen (name.sun_path) + 1);
-
-  if (bind (sock, (struct sockaddr *) &name, size) < 0)
-    {
-      perror (_("ERROR: bind"));
-      return -1;
-    }
-#endif
-
   return sock;
 }
 
-void
+static void
 termination_handler (int signum)
 {
@@ -138,17 +116,26 @@
 	fprintf(stdout, _("# Terminated on signal %d\n"), signum);
     }
-#if 0
-  if (sockname != NULL) unlink (sockname);
-#endif
-  if (sock   >= 0 ) close  (sock);
-
+  if (sock   >= 0 ) 
+    close  (sock);
   return;
 }
 
-
-int send_to_server (char * serversock, char * message)
+static char * safe_copy(char * to, const char * from, size_t size)
+{
+  if (to && from)
+    {
+      strncpy (to, from, size);
+      if (size > 0)
+	to[size-1] = '\0';
+      else 
+	*to = '\0';
+    }
+  return to;
+}
+ 
+
+static int send_to_server (char * serversock, char * message)
 {
   struct sockaddr_un name;
-  /* size_t size; */
   int size;
   int nbytes;
@@ -168,10 +155,7 @@
     }
 
-  /* Send the datagram. 
-  nbytes = sendto (sock, message, strlen (message) + 1, 0,
-                   (struct sockaddr *) & name, size);
+  /* Send the data. 
    */
   nbytes = send (sock, message, strlen (message) + 1, 0);
-
   if (nbytes < 0)
     {
@@ -205,5 +189,5 @@
 }
 
-int recv_from_server (char * message)
+static int recv_from_server (char * message)
 {
   int nbytes = 0;
@@ -211,48 +195,20 @@
   int  num = 0;
   int  good = -1;
-  int  islist = 0;
   char * p;
 
   if (password[0] == '\0')
-    {
-      if (message[0] == 'L' && message[1] == 'I' &&
-	  message[2] == 'S' && message[3] == 'T')
-	{
-	  islist = 1;
-	}
-      if (message[0] == 'P' && message[1] == 'R' &&
-	  message[2] == 'O' && message[3] == 'B' && message[4] == 'E' )
-	{
-	  islist = 1;
-	}
-    }
+    p = message;
   else
-    {
-      p = &message[strlen(password)+1];
-      if (p[0] == 'L' && p[1] == 'I' &&
-	  p[2] == 'S' && p[3] == 'T')
-	{
-	  islist = 1;
-	}
-      if (p[0] == 'P' && p[1] == 'R' &&
-	  p[2] == 'O' && p[3] == 'B' && p[4] == 'E' )
-	{
-	  islist = 1;
-	}
-    }
-
-  if (islist == 1)
+    p = &message[strlen(password)+1];
+
+  if (0 == strncmp(p, _("PROBE"), 5) ||
+      0 == strncmp(p, _("LIST"),  4))
     {
       do {
-	/*
-	nbytes = recvfrom (sock, recvmsg, SH_MAXMSG, 0, NULL, 0);
-	*/
 	nbytes = getline_from_server (sock, recvmsg, SH_MAXMSG);
 	if (nbytes < 0)
 	  {
 	    if (errno == EAGAIN)
-	      {
-		return 0;
-	      }
+	      return 0;
 	    else
 	      {
@@ -262,7 +218,6 @@
 	  }
 	else if (nbytes == 0)
-	  {
-	    return 0;
-	  }
+	  return 0;
+
 	if (recvmsg[0] == 'E' && recvmsg[1] == 'N' && recvmsg[2] == 'D')
 	  {
@@ -277,7 +232,4 @@
   else
     {
-      /*
-      nbytes = recvfrom (sock, recvmsg, SH_MAXMSG, 0, NULL, 0);
-      */
       nbytes = recv (sock, recvmsg, SH_MAXMSG, 0);
       if (nbytes < 0)
@@ -290,15 +242,19 @@
   /* Print a diagnostic message. */
   if (password[0] == '\0')
-    {
-      good = strcmp (message, recvmsg);
-    }
+    good = strcmp (message, recvmsg);
   else
-    {
-      good = strcmp (&message[strlen(password)+1], recvmsg);
-    }
+    good = strcmp (&message[strlen(password)+1], recvmsg);
 
   if (0 != good)
     {
-      fprintf (stderr, "%s", _("ERROR: Bounced message != original message (possible reason: superfluous password).\n"));
+      if (0 == strncmp(recvmsg, _("!E:"), 3))
+	{ 
+	  fputs(recvmsg, stderr); 
+	  fputc('\n', stderr); 
+	}
+      else
+	{
+	  fputs (_("ERROR: Bounced message != original message.\n"), stderr);
+	}
       return -1;
     }
@@ -312,5 +268,60 @@
 }
 
-void usage(char * name)
+static int check_uuid(const char * in)
+{
+  int 		i;
+  const char	*cp;
+
+  if (!in || strlen(in) != 36)
+    return -1;
+  for (i=0, cp = in; i <= 36; i++,cp++) {
+    if ((i == 8) || (i == 13) || (i == 18) ||
+	(i == 23)) {
+      if (*cp == '-')
+	continue;
+      else
+	return -1;
+    }
+    if (i== 36)
+      if (*cp == 0)
+	continue;
+    if (!isxdigit(*cp))
+      return -1;
+  }
+  return 0;
+}
+
+static int check_command(const char * str)
+{
+  unsigned int i = 0;
+  char * commands[] = { N_("DELTA:"), N_("RELOAD"),  N_("STOP"), N_("SCAN"),
+			N_("CANCEL"), N_("LISTALL"), N_("LIST"), N_("PROBE"), NULL };
+
+  while (commands[i])
+    {
+      size_t len = strlen(_(commands[i]));
+
+      if (0 == strncmp(_(commands[i]), str, len))
+	{
+	  if (i == 0)
+	    {
+	      char * p = strchr(str, ':'); ++p;
+	      if ( 0 == check_uuid(p) )
+		return 0;
+	    }
+	  else
+	    {
+	      if (len == strlen(str))
+		return 0;
+	    }
+	}
+      ++i;
+    }
+
+  fprintf (stderr, _("ERROR: invalid command <%s>\n\n"), str);
+  return -1;
+}
+
+static void print_usage_and_exit(char * name, int exit_status)
 {
   printf(_("\nUsage : %s [-v][-s server_socket] -c command <client_hostname>\n\n"), 
@@ -325,12 +336,13 @@
   printf("%s", _("          variable YULECTL_PASSWORD (not recommended).\n\n"));
 
-  printf("%s", _("Commands: RELOAD    <reload configuration>\n"));
-  printf("%s", _("          STOP      <terminate>\n"));
-  printf("%s", _("          SCAN      <initiate file system check\n"));
-  printf("%s", _("          CANCEL    <cancel previous command>\n"));
-  printf("%s", _("          LIST      <list queued commands>\n"));
-  printf("%s", _("          LISTALL   <list queued and last sent commands>\n"));
-  printf("%s", _("          PROBE     <probe all clients for necessity of reload>\n"));
-  return;
+  printf("%s", _("Commands: RELOAD         reload configuration\n"));
+  printf("%s", _("          DELTA:<uuid>   load delta database with given uuid\n"));
+  printf("%s", _("          STOP           terminate\n"));
+  printf("%s", _("          SCAN           initiate file system check\n"));
+  printf("%s", _("          CANCEL         cancel pending command(s)\n"));
+  printf("%s", _("          LIST           list queued commands\n"));
+  printf("%s", _("          LISTALL        list queued and last sent commands\n"));
+  printf("%s", _("          PROBE          probe all clients for necessity of reload\n"));
+  exit(exit_status);
 }
 
@@ -339,6 +351,5 @@
   size_t len;
 
-  if (!str)
-    return str;
+  if (!str) return str;
 
   len = strlen(str);
@@ -351,24 +362,11 @@
 	break;
     }
-    
   return str;
 }
 
-void fixup_message (char * message)
-{
-  char message2[SH_MAXMSG];
-  char home[4096];
-  FILE * fp;
+static int get_home(char * home, size_t size)
+{
   struct passwd * pwent;
-  char * pw;
-
-  pw = getenv(_("YULECTL_PASSWORD"));
-  if (pw && strlen(pw) < 15)
-    {
-      strcpy(password, pw);
-      strcpy(message2, password);
-      goto do_msg;
-    }
-  
+
   pwent = getpwuid(geteuid());
   if ((pwent == 0) || (pwent->pw_dir == NULL))
@@ -379,28 +377,49 @@
       if (NULL != getenv(_("HOME")))
 	{
-	  strncpy(home, getenv(_("HOME")), 4096);
-	  home[4095] = '\0';
+	  safe_copy(home, getenv(_("HOME")), size);
 	}
       else
 	{
 	  fprintf (stderr, _("ERROR: no home directory for euid %ld (tried $HOME and password database).\n"), (long) geteuid());
-	  exit(EXIT_FAILURE);
+	  return -1;
 	}
     }
   else
     {
-      strncpy(home, pwent->pw_dir, 4096);
-      home[4095] = '\0';
-    }
+      safe_copy(home, pwent->pw_dir, size);
+    }
+  return 0;
+}
+
+static int get_passwd(char * message2, size_t size)
+{
+  char home[4096];
+  FILE * fp;
+  char * pw;
+
+  /* 1) Password from environment
+   */
+  pw = getenv(_("YULECTL_PASSWORD"));
+  if (pw && strlen(pw) < 15)
+    {
+      strcpy(password, pw);
+      strcpy(message2, password);
+      return 0;
+    }
+
+  /* 2) Password from $HOME/.yule_cred
+   */
+  if (get_home(home, sizeof(home)) < 0)
+    return -1;
 
   if ( (strlen(home) + strlen(_("/.yulectl_cred")) + 1) > 4096)
     {
       fprintf (stderr, "%s", _("ERROR: path for $HOME is too long.\n"));
-      exit(EXIT_FAILURE);
+      return -1;
     }
   strcat(home, _("/.yulectl_cred"));
   fp = fopen(home, "r");
 
-#if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED) && !defined(HAVE_STRUCT_CMSGCRED) && !defined(HAVE_STRUCT_FCRED) && !(defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
+#if defined(SH_REQ_PASSWORD)
   if (fp == NULL)
     {
@@ -415,17 +434,17 @@
 		 home, (long)geteuid(), (long)getuid());
       }
-      exit(EXIT_FAILURE);
+      return -1;
     }
 #else
   if (fp == NULL)
-    return;
+    return 0;
 #endif
 
-  if (NULL == fgets(message2, sizeof(message2), fp))
+  if (NULL == fgets(message2, size, fp))
     {
       fprintf (stderr,
 	       _("ERROR: empty or unreadable password file (%s).\n"),
 	       home);
-      exit(EXIT_FAILURE);
+      return -1;
     }
 
@@ -436,19 +455,73 @@
       fprintf (stderr, "%s", 
 	       _("ERROR: Password too long (max. 14 characters).\n"));
-      exit(EXIT_FAILURE);
-    }
-
+      return -1;
+    }
   strcpy(password, message2);
   fclose(fp);
 
- do_msg:
-  strcat(message2, "@");
-
-  strncat(message2, message, SH_MAXMSG - strlen(message2) -1);
-  message2[SH_MAXMSG-1] = '\0';
-  strcpy(message, message2);
+  return 0;
+}
+
+static int fixup_message (char * message)
+{
+  char message_fixed[SH_MAXMSG] = { 0 };
+
+  if (get_passwd(message_fixed, sizeof(message_fixed)) < 0)
+    return -1;
+
+  if (strlen(message_fixed) > 0)
+    {
+      strcat(message_fixed, "@");
+
+      strncat(message_fixed, message, SH_MAXMSG - strlen(message_fixed) -1);
+      message_fixed[SH_MAXMSG-1] = '\0';
+      strcpy(message, message_fixed);
+    }
+  return 0;
+}
+
+static int fill_serversock(char * serversock, size_t size)
+{
+  int status;
+
+#ifdef HAVE_VSNPRINTF
+  status = snprintf(serversock, size, _("%s/%s.sock"), 
+		    DEFAULT_PIDDIR, SH_INSTALL_NAME);
+#else
+  if ((strlen(DEFAULT_PIDDIR) + strlen(SH_INSTALL_NAME) + 1 + 6) > size)
+    status = -1;
+  else
+    status = sprintf (serversock, _("%s/%s.sock"), 
+		      DEFAULT_PIDDIR, SH_INSTALL_NAME);
+#endif
+
+  if ((status < 0) || (status > (int)(size-1)))
+    {
+      fprintf(stderr, _("ERROR: Path too long (maximum %d): %s/%s.sock\n"), 
+	      (int) (size-1), DEFAULT_PIDDIR, SH_INSTALL_NAME);
+      return -1;
+    }
+  return 0;
+}
+
+static void checklen(char * command, char * str, size_t maxlen)
+{
+  if (strlen(str) > maxlen) 
+    {
+      fprintf(stderr, _("ERROR: String too long (max %d): %s\n\n"), 
+	      (int) maxlen, str);
+      print_usage_and_exit (command, EXIT_FAILURE);
+    }
   return;
 }
 
+static void checknull(char * command, char * str)
+{
+  if (str == NULL || str[0] == '\0') {
+    fprintf(stderr, "%s", _("ERROR: option with missing argument\n\n"));
+    print_usage_and_exit(command, EXIT_FAILURE);
+  }
+  return;
+}
 
 int
@@ -457,31 +530,12 @@
 
   char   message[SH_MAXMSG] = "";
-  char   clientcd[1024];
   char   serversock[256];
-  int    status, size;
+  int    status;
   int    num = 1;
   int    flag = 0;
-  
-#ifdef HAVE_VSNPRINTF
-  status = snprintf(serversock, 256, _("%s/%s.sock"), 
-		    DEFAULT_PIDDIR, SH_INSTALL_NAME);
-#else
-  if ((strlen(DEFAULT_PIDDIR) + strlen(SH_INSTALL_NAME) + 1 + 6) > 256)
-    {
-      status = -1;
-    }
-  else
-    {
-      status = sprintf (serversock, _("%s/%s.sock"), 
-			DEFAULT_PIDDIR, SH_INSTALL_NAME);
-    }
-#endif
-
-  if ((status < 0) || (status > 255))
-    {
-      fprintf(stderr, _("ERROR: Path too long (maximum 255): %s/%s.sock\n"), 
-	      DEFAULT_PIDDIR, SH_INSTALL_NAME);
-      return (EXIT_FAILURE);
-    }
+
+  if (fill_serversock(serversock, sizeof(serversock)) < 0)
+    return (EXIT_FAILURE);
+
 
   while (argc > 1 && argv[num][0] == '-')
@@ -490,53 +544,30 @@
 	{
 	  case 'h':
-	    usage(argv[0]);
-	    return (EXIT_SUCCESS);
-
+	    print_usage_and_exit(argv[0], EXIT_SUCCESS);
+	    break;
 	  case 'v':
 	    ++verbose;
 	    break;
-
 	  case 's':
 	    --argc; ++num;
-	    if (argv[num] == NULL || argv[num][0] == '\0') {
-	      usage(argv[0]);
-	      fprintf(stderr, "%s", _("ERROR: -s: argument missing\n"));
-	      return (EXIT_FAILURE);
-	    } else {
-	      if (strlen(argv[num]) > 255) 
-		{
-		  fprintf(stderr, _("ERROR: Path too long: %s\n"), argv[num]);
-		  return (EXIT_FAILURE);
-		}
-	      strncpy (serversock, argv[num], 256);
-	      serversock[255] = '\0';
-	    }
+	    checknull(argv[0], argv[num]);
+	    checklen(argv[0], argv[num], sizeof(serversock)-1);
+	    safe_copy (serversock, argv[num], sizeof(serversock));
 	    break;
-
 	  case 'c':
 	    --argc; ++num;
-	    if (argv[num] == NULL || argv[num][0] == '\0') {
-	      usage(argv[0]);
-	      fprintf(stderr, "%s", _("ERROR: -c: argument missing\n"));
-	      return (EXIT_FAILURE);
-	    } else {
-	      if (strlen(argv[num]) >= SH_MAXMSG) 
-		{
-		  fprintf(stderr, _("ERROR: Command too long: %s\n"), 
-			  argv[num]);
-		  return (EXIT_FAILURE);
-		}
-	      strncpy (message, argv[num], SH_MAXMSG);
-	      message[SH_MAXMSG-1] = '\0';
-	      strncat(message, ":", SH_MAXMSG-strlen(message)-1);
-	      message[SH_MAXMSG-1] = '\0';
-	      flag = 1;
-	    }
+	    checknull(argv[0], argv[num]);
+	    checklen(argv[0], argv[num], SH_MAXMSG-1);
+	    if (0 != check_command(argv[num]))
+	      print_usage_and_exit(argv[0], EXIT_FAILURE);
+	    safe_copy(message, argv[num], SH_MAXMSG);
+	    strncat(message, ":", SH_MAXMSG-strlen(message)-1);
+	    message[SH_MAXMSG-1] = '\0';
+	    flag = 1;
 	    break;
-
 	  default:
-	    usage(argv[0]);
-	    fprintf(stderr, _("ERROR: unknown option -%c\n"), argv[num][1]);
-	    return (EXIT_FAILURE);
+	    fprintf(stderr, _("ERROR: unknown option -%c\n\n"), argv[num][1]);
+	    print_usage_and_exit(argv[0], EXIT_FAILURE);
+	    break;
 	}
       --argc; ++num;
@@ -544,70 +575,35 @@
 
   if (flag == 0) /* no command given */
-    {
-      usage(argv[0]);
-      return (EXIT_FAILURE);
-    }
+    print_usage_and_exit(argv[0], EXIT_FAILURE);
 
   if (argc > 1)
     {
-      if (strlen(argv[num]) > (SH_MAXMSG - strlen(message) -1)) 
-	{
-	  fprintf(stderr, _("ERROR: Hostname too long: %s\n"), argv[num]);
-	  return (EXIT_FAILURE);
-	}
-      strncat (message, argv[num], SH_MAXMSG -strlen(message) - 1);
+      checklen(argv[0], argv[num], SH_MAXMSG - strlen(message) - 1);
+      strncat (message, argv[num], SH_MAXMSG - strlen(message) - 1);
       message[SH_MAXMSG-1] = '\0';
     }
   else
     {
-      if (message[0] == 'P' && message[1] == 'R' &&
-	  message[2] == 'O' && message[3] == 'B' && message[4] == 'E' )
+      if (0 == strncmp(message, _("PROBE"), 5) ||
+	  0 == strncmp(message, _("LIST"),  4))
 	{
 	  strncat (message, _("dummy"), SH_MAXMSG -strlen(message) - 1);
 	  message[SH_MAXMSG-1] = '\0';
 	}
-      else if (message[0] == 'L' && message[1] == 'I' &&
-	       message[2] == 'S' && message[3] == 'T')
-	{
-	  strncat (message, _("dummy"), SH_MAXMSG -strlen(message) - 1);
-	  message[SH_MAXMSG-1] = '\0';
-	}
       else
 	{
 	  fprintf(stderr, "%s", _("ERROR: this command requires a hostname\n"));
-	  usage(argv[0]);
-	  return (EXIT_FAILURE);
-	}
-    }
-
-  fixup_message(message);
-
-  /* OpenBSD wants >= 1024
-   */
-  if (NULL == getcwd(clientcd, 1024))
-    {
-      perror(_("ERROR: getcwd"));
-      return (EXIT_FAILURE);
-    }
-  size = strlen(clientcd) + 1 + strlen(CLIENT) + 6;
-  sockname = calloc(1,size);
-  if (!sockname)
-    {
-      perror(_("ERROR: main: malloc"));
-      return (EXIT_FAILURE);
-    }
-#ifdef HAVE_VSNPRINTF
-  snprintf(sockname, size, _("%s/%s.sock"), clientcd, CLIENT);
-#else
-  sprintf(sockname, _("%s/%s.sock"), clientcd, CLIENT);
-#endif
+	  print_usage_and_exit(argv[0], EXIT_FAILURE);
+	}
+    }
+
+  if (fixup_message(message) < 0)
+    return (EXIT_FAILURE);
 
   /* Make the socket.
    */
-  sock = make_named_socket (sockname);
+  sock = create_unix_socket ();
   if (sock < 0)
-    {
-      return (EXIT_FAILURE);
-    }
+    return (EXIT_FAILURE);
 
   /* Set up termination handler.
@@ -630,20 +626,17 @@
   /* Wait for a reply. 
    */
-  if (message[0] == 'L' && message[1] == 'I' &&
-      message[2] == 'S' && message[3] == 'T')
-    {
-      if (verbose)
+  if (verbose)
+    {
+      if (0 == strncmp(message, "LIST", 4))
 	fprintf(stdout, "%s", _("# Waiting for listing.\n"));
-    }
-  else
-    {
-      if (verbose)
+      else
 	fprintf(stdout, "%s", _("# Waiting for confirmation.\n"));
     }
+
   status = recv_from_server (message);
 
   if (status < 0)
     {
-      fprintf(stderr, "%s", _("ERROR: receiving data from server failed.\n"));
+      fputs(_("ERROR: unexpected or no reply from server.\n"), stderr);
       (void) termination_handler(0);
       return (EXIT_FAILURE);
