Changeset 481 for trunk/src/yulectl.c


Ignore:
Timestamp:
Jul 18, 2015, 5:06:52 PM (6 years ago)
Author:
katerina
Message:

Enhancements and fixes for tickets #374, #375, #376, #377, #378, and #379.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/yulectl.c

    r454 r481  
    2424#include <stdlib.h>
    2525#include <string.h>
     26#include <ctype.h>
    2627#include <errno.h>
    2728
     
    4142#define SH_MAXMSG 209
    4243
     44#if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED) && \
     45  !defined(HAVE_STRUCT_CMSGCRED) && !defined(HAVE_STRUCT_FCRED) && \
     46  !(defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
     47#define SH_REQ_PASSWORD 1
     48#endif
     49
    4350static int    sock     = -1;
    44 static char * sockname = NULL;
    45 
    4651static char   password[15] = "";
    47 
    4852static int    verbose = 0;
    4953
     
    8690
    8791
    88 int
    89 make_named_socket (char * sockname)
     92static int
     93create_unix_socket ()
    9094{
    9195  int sock;
    92 
    93 #if 0
    94   struct sockaddr_un name;
    95   size_t size;
    96 #else
    97   (void) sockname;
    98 #endif
    9996
    10097  /* Create the socket. */
     
    107104    }
    108105
    109 #if 0
    110   /* Bind a name to the socket. */
    111   name.sun_family = AF_FILE;
    112   strcpy (name.sun_path, sockname);
    113 
    114   /* The size of the address is
    115      the offset of the start of the filename,
    116      plus its length,
    117      plus one for the terminating null byte. */
    118   size = (offsetof (struct sockaddr_un, sun_path)
    119           + strlen (name.sun_path) + 1);
    120 
    121   if (bind (sock, (struct sockaddr *) &name, size) < 0)
    122     {
    123       perror (_("ERROR: bind"));
    124       return -1;
    125     }
    126 #endif
    127 
    128106  return sock;
    129107}
    130108
    131 void
     109static void
    132110termination_handler (int signum)
    133111{
     
    138116        fprintf(stdout, _("# Terminated on signal %d\n"), signum);
    139117    }
    140 #if 0
    141   if (sockname != NULL) unlink (sockname);
    142 #endif
    143   if (sock   >= 0 ) close  (sock);
    144 
     118  if (sock   >= 0 )
     119    close  (sock);
    145120  return;
    146121}
    147122
    148 
    149 int send_to_server (char * serversock, char * message)
     123static char * safe_copy(char * to, const char * from, size_t size)
     124{
     125  if (to && from)
     126    {
     127      strncpy (to, from, size);
     128      if (size > 0)
     129        to[size-1] = '\0';
     130      else
     131        *to = '\0';
     132    }
     133  return to;
     134}
     135 
     136
     137static int send_to_server (char * serversock, char * message)
    150138{
    151139  struct sockaddr_un name;
    152   /* size_t size; */
    153140  int size;
    154141  int nbytes;
     
    168155    }
    169156
    170   /* Send the datagram.
    171   nbytes = sendto (sock, message, strlen (message) + 1, 0,
    172                    (struct sockaddr *) & name, size);
     157  /* Send the data.
    173158   */
    174159  nbytes = send (sock, message, strlen (message) + 1, 0);
    175 
    176160  if (nbytes < 0)
    177161    {
     
    205189}
    206190
    207 int recv_from_server (char * message)
     191static int recv_from_server (char * message)
    208192{
    209193  int nbytes = 0;
     
    211195  int  num = 0;
    212196  int  good = -1;
    213   int  islist = 0;
    214197  char * p;
    215198
    216199  if (password[0] == '\0')
    217     {
    218       if (message[0] == 'L' && message[1] == 'I' &&
    219           message[2] == 'S' && message[3] == 'T')
    220         {
    221           islist = 1;
    222         }
    223       if (message[0] == 'P' && message[1] == 'R' &&
    224           message[2] == 'O' && message[3] == 'B' && message[4] == 'E' )
    225         {
    226           islist = 1;
    227         }
    228     }
     200    p = message;
    229201  else
    230     {
    231       p = &message[strlen(password)+1];
    232       if (p[0] == 'L' && p[1] == 'I' &&
    233           p[2] == 'S' && p[3] == 'T')
    234         {
    235           islist = 1;
    236         }
    237       if (p[0] == 'P' && p[1] == 'R' &&
    238           p[2] == 'O' && p[3] == 'B' && p[4] == 'E' )
    239         {
    240           islist = 1;
    241         }
    242     }
    243 
    244   if (islist == 1)
     202    p = &message[strlen(password)+1];
     203
     204  if (0 == strncmp(p, _("PROBE"), 5) ||
     205      0 == strncmp(p, _("LIST"),  4))
    245206    {
    246207      do {
    247         /*
    248         nbytes = recvfrom (sock, recvmsg, SH_MAXMSG, 0, NULL, 0);
    249         */
    250208        nbytes = getline_from_server (sock, recvmsg, SH_MAXMSG);
    251209        if (nbytes < 0)
    252210          {
    253211            if (errno == EAGAIN)
    254               {
    255                 return 0;
    256               }
     212              return 0;
    257213            else
    258214              {
     
    262218          }
    263219        else if (nbytes == 0)
    264           {
    265             return 0;
    266           }
     220          return 0;
     221
    267222        if (recvmsg[0] == 'E' && recvmsg[1] == 'N' && recvmsg[2] == 'D')
    268223          {
     
    277232  else
    278233    {
    279       /*
    280       nbytes = recvfrom (sock, recvmsg, SH_MAXMSG, 0, NULL, 0);
    281       */
    282234      nbytes = recv (sock, recvmsg, SH_MAXMSG, 0);
    283235      if (nbytes < 0)
     
    290242  /* Print a diagnostic message. */
    291243  if (password[0] == '\0')
    292     {
    293       good = strcmp (message, recvmsg);
    294     }
     244    good = strcmp (message, recvmsg);
    295245  else
    296     {
    297       good = strcmp (&message[strlen(password)+1], recvmsg);
    298     }
     246    good = strcmp (&message[strlen(password)+1], recvmsg);
    299247
    300248  if (0 != good)
    301249    {
    302       fprintf (stderr, "%s", _("ERROR: Bounced message != original message (possible reason: superfluous password).\n"));
     250      if (0 == strncmp(recvmsg, _("!E:"), 3))
     251        {
     252          fputs(recvmsg, stderr);
     253          fputc('\n', stderr);
     254        }
     255      else
     256        {
     257          fputs (_("ERROR: Bounced message != original message.\n"), stderr);
     258        }
    303259      return -1;
    304260    }
     
    312268}
    313269
    314 void usage(char * name)
     270static int check_uuid(const char * in)
     271{
     272  int           i;
     273  const char    *cp;
     274
     275  if (!in || strlen(in) != 36)
     276    return -1;
     277  for (i=0, cp = in; i <= 36; i++,cp++) {
     278    if ((i == 8) || (i == 13) || (i == 18) ||
     279        (i == 23)) {
     280      if (*cp == '-')
     281        continue;
     282      else
     283        return -1;
     284    }
     285    if (i== 36)
     286      if (*cp == 0)
     287        continue;
     288    if (!isxdigit(*cp))
     289      return -1;
     290  }
     291  return 0;
     292}
     293
     294static int check_command(const char * str)
     295{
     296  unsigned int i = 0;
     297  char * commands[] = { N_("DELTA:"), N_("RELOAD"),  N_("STOP"), N_("SCAN"),
     298                        N_("CANCEL"), N_("LISTALL"), N_("LIST"), N_("PROBE"), NULL };
     299
     300  while (commands[i])
     301    {
     302      size_t len = strlen(_(commands[i]));
     303
     304      if (0 == strncmp(_(commands[i]), str, len))
     305        {
     306          if (i == 0)
     307            {
     308              char * p = strchr(str, ':'); ++p;
     309              if ( 0 == check_uuid(p) )
     310                return 0;
     311            }
     312          else
     313            {
     314              if (len == strlen(str))
     315                return 0;
     316            }
     317        }
     318      ++i;
     319    }
     320
     321  fprintf (stderr, _("ERROR: invalid command <%s>\n\n"), str);
     322  return -1;
     323}
     324
     325static void print_usage_and_exit(char * name, int exit_status)
    315326{
    316327  printf(_("\nUsage : %s [-v][-s server_socket] -c command <client_hostname>\n\n"),
     
    325336  printf("%s", _("          variable YULECTL_PASSWORD (not recommended).\n\n"));
    326337
    327   printf("%s", _("Commands: RELOAD    <reload configuration>\n"));
    328   printf("%s", _("          STOP      <terminate>\n"));
    329   printf("%s", _("          SCAN      <initiate file system check\n"));
    330   printf("%s", _("          CANCEL    <cancel previous command>\n"));
    331   printf("%s", _("          LIST      <list queued commands>\n"));
    332   printf("%s", _("          LISTALL   <list queued and last sent commands>\n"));
    333   printf("%s", _("          PROBE     <probe all clients for necessity of reload>\n"));
    334   return;
     338  printf("%s", _("Commands: RELOAD         reload configuration\n"));
     339  printf("%s", _("          DELTA:<uuid>   load delta database with given uuid\n"));
     340  printf("%s", _("          STOP           terminate\n"));
     341  printf("%s", _("          SCAN           initiate file system check\n"));
     342  printf("%s", _("          CANCEL         cancel pending command(s)\n"));
     343  printf("%s", _("          LIST           list queued commands\n"));
     344  printf("%s", _("          LISTALL        list queued and last sent commands\n"));
     345  printf("%s", _("          PROBE          probe all clients for necessity of reload\n"));
     346  exit(exit_status);
    335347}
    336348
     
    339351  size_t len;
    340352
    341   if (!str)
    342     return str;
     353  if (!str) return str;
    343354
    344355  len = strlen(str);
     
    351362        break;
    352363    }
    353    
    354364  return str;
    355365}
    356366
    357 void fixup_message (char * message)
    358 {
    359   char message2[SH_MAXMSG];
    360   char home[4096];
    361   FILE * fp;
     367static int get_home(char * home, size_t size)
     368{
    362369  struct passwd * pwent;
    363   char * pw;
    364 
    365   pw = getenv(_("YULECTL_PASSWORD"));
    366   if (pw && strlen(pw) < 15)
    367     {
    368       strcpy(password, pw);
    369       strcpy(message2, password);
    370       goto do_msg;
    371     }
    372  
     370
    373371  pwent = getpwuid(geteuid());
    374372  if ((pwent == 0) || (pwent->pw_dir == NULL))
     
    379377      if (NULL != getenv(_("HOME")))
    380378        {
    381           strncpy(home, getenv(_("HOME")), 4096);
    382           home[4095] = '\0';
     379          safe_copy(home, getenv(_("HOME")), size);
    383380        }
    384381      else
    385382        {
    386383          fprintf (stderr, _("ERROR: no home directory for euid %ld (tried $HOME and password database).\n"), (long) geteuid());
    387           exit(EXIT_FAILURE);
     384          return -1;
    388385        }
    389386    }
    390387  else
    391388    {
    392       strncpy(home, pwent->pw_dir, 4096);
    393       home[4095] = '\0';
    394     }
     389      safe_copy(home, pwent->pw_dir, size);
     390    }
     391  return 0;
     392}
     393
     394static int get_passwd(char * message2, size_t size)
     395{
     396  char home[4096];
     397  FILE * fp;
     398  char * pw;
     399
     400  /* 1) Password from environment
     401   */
     402  pw = getenv(_("YULECTL_PASSWORD"));
     403  if (pw && strlen(pw) < 15)
     404    {
     405      strcpy(password, pw);
     406      strcpy(message2, password);
     407      return 0;
     408    }
     409
     410  /* 2) Password from $HOME/.yule_cred
     411   */
     412  if (get_home(home, sizeof(home)) < 0)
     413    return -1;
    395414
    396415  if ( (strlen(home) + strlen(_("/.yulectl_cred")) + 1) > 4096)
    397416    {
    398417      fprintf (stderr, "%s", _("ERROR: path for $HOME is too long.\n"));
    399       exit(EXIT_FAILURE);
     418      return -1;
    400419    }
    401420  strcat(home, _("/.yulectl_cred"));
    402421  fp = fopen(home, "r");
    403422
    404 #if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED) && !defined(HAVE_STRUCT_CMSGCRED) && !defined(HAVE_STRUCT_FCRED) && !(defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
     423#if defined(SH_REQ_PASSWORD)
    405424  if (fp == NULL)
    406425    {
     
    415434                 home, (long)geteuid(), (long)getuid());
    416435      }
    417       exit(EXIT_FAILURE);
     436      return -1;
    418437    }
    419438#else
    420439  if (fp == NULL)
    421     return;
     440    return 0;
    422441#endif
    423442
    424   if (NULL == fgets(message2, sizeof(message2), fp))
     443  if (NULL == fgets(message2, size, fp))
    425444    {
    426445      fprintf (stderr,
    427446               _("ERROR: empty or unreadable password file (%s).\n"),
    428447               home);
    429       exit(EXIT_FAILURE);
     448      return -1;
    430449    }
    431450
     
    436455      fprintf (stderr, "%s",
    437456               _("ERROR: Password too long (max. 14 characters).\n"));
    438       exit(EXIT_FAILURE);
    439     }
    440 
     457      return -1;
     458    }
    441459  strcpy(password, message2);
    442460  fclose(fp);
    443461
    444  do_msg:
    445   strcat(message2, "@");
    446 
    447   strncat(message2, message, SH_MAXMSG - strlen(message2) -1);
    448   message2[SH_MAXMSG-1] = '\0';
    449   strcpy(message, message2);
     462  return 0;
     463}
     464
     465static int fixup_message (char * message)
     466{
     467  char message_fixed[SH_MAXMSG] = { 0 };
     468
     469  if (get_passwd(message_fixed, sizeof(message_fixed)) < 0)
     470    return -1;
     471
     472  if (strlen(message_fixed) > 0)
     473    {
     474      strcat(message_fixed, "@");
     475
     476      strncat(message_fixed, message, SH_MAXMSG - strlen(message_fixed) -1);
     477      message_fixed[SH_MAXMSG-1] = '\0';
     478      strcpy(message, message_fixed);
     479    }
     480  return 0;
     481}
     482
     483static int fill_serversock(char * serversock, size_t size)
     484{
     485  int status;
     486
     487#ifdef HAVE_VSNPRINTF
     488  status = snprintf(serversock, size, _("%s/%s.sock"),
     489                    DEFAULT_PIDDIR, SH_INSTALL_NAME);
     490#else
     491  if ((strlen(DEFAULT_PIDDIR) + strlen(SH_INSTALL_NAME) + 1 + 6) > size)
     492    status = -1;
     493  else
     494    status = sprintf (serversock, _("%s/%s.sock"),
     495                      DEFAULT_PIDDIR, SH_INSTALL_NAME);
     496#endif
     497
     498  if ((status < 0) || (status > (int)(size-1)))
     499    {
     500      fprintf(stderr, _("ERROR: Path too long (maximum %d): %s/%s.sock\n"),
     501              (int) (size-1), DEFAULT_PIDDIR, SH_INSTALL_NAME);
     502      return -1;
     503    }
     504  return 0;
     505}
     506
     507static void checklen(char * command, char * str, size_t maxlen)
     508{
     509  if (strlen(str) > maxlen)
     510    {
     511      fprintf(stderr, _("ERROR: String too long (max %d): %s\n\n"),
     512              (int) maxlen, str);
     513      print_usage_and_exit (command, EXIT_FAILURE);
     514    }
    450515  return;
    451516}
    452517
     518static void checknull(char * command, char * str)
     519{
     520  if (str == NULL || str[0] == '\0') {
     521    fprintf(stderr, "%s", _("ERROR: option with missing argument\n\n"));
     522    print_usage_and_exit(command, EXIT_FAILURE);
     523  }
     524  return;
     525}
    453526
    454527int
     
    457530
    458531  char   message[SH_MAXMSG] = "";
    459   char   clientcd[1024];
    460532  char   serversock[256];
    461   int    status, size;
     533  int    status;
    462534  int    num = 1;
    463535  int    flag = 0;
    464  
    465 #ifdef HAVE_VSNPRINTF
    466   status = snprintf(serversock, 256, _("%s/%s.sock"),
    467                     DEFAULT_PIDDIR, SH_INSTALL_NAME);
    468 #else
    469   if ((strlen(DEFAULT_PIDDIR) + strlen(SH_INSTALL_NAME) + 1 + 6) > 256)
    470     {
    471       status = -1;
    472     }
    473   else
    474     {
    475       status = sprintf (serversock, _("%s/%s.sock"),
    476                         DEFAULT_PIDDIR, SH_INSTALL_NAME);
    477     }
    478 #endif
    479 
    480   if ((status < 0) || (status > 255))
    481     {
    482       fprintf(stderr, _("ERROR: Path too long (maximum 255): %s/%s.sock\n"),
    483               DEFAULT_PIDDIR, SH_INSTALL_NAME);
    484       return (EXIT_FAILURE);
    485     }
     536
     537  if (fill_serversock(serversock, sizeof(serversock)) < 0)
     538    return (EXIT_FAILURE);
     539
    486540
    487541  while (argc > 1 && argv[num][0] == '-')
     
    490544        {
    491545          case 'h':
    492             usage(argv[0]);
    493             return (EXIT_SUCCESS);
    494 
     546            print_usage_and_exit(argv[0], EXIT_SUCCESS);
     547            break;
    495548          case 'v':
    496549            ++verbose;
    497550            break;
    498 
    499551          case 's':
    500552            --argc; ++num;
    501             if (argv[num] == NULL || argv[num][0] == '\0') {
    502               usage(argv[0]);
    503               fprintf(stderr, "%s", _("ERROR: -s: argument missing\n"));
    504               return (EXIT_FAILURE);
    505             } else {
    506               if (strlen(argv[num]) > 255)
    507                 {
    508                   fprintf(stderr, _("ERROR: Path too long: %s\n"), argv[num]);
    509                   return (EXIT_FAILURE);
    510                 }
    511               strncpy (serversock, argv[num], 256);
    512               serversock[255] = '\0';
    513             }
     553            checknull(argv[0], argv[num]);
     554            checklen(argv[0], argv[num], sizeof(serversock)-1);
     555            safe_copy (serversock, argv[num], sizeof(serversock));
    514556            break;
    515 
    516557          case 'c':
    517558            --argc; ++num;
    518             if (argv[num] == NULL || argv[num][0] == '\0') {
    519               usage(argv[0]);
    520               fprintf(stderr, "%s", _("ERROR: -c: argument missing\n"));
    521               return (EXIT_FAILURE);
    522             } else {
    523               if (strlen(argv[num]) >= SH_MAXMSG)
    524                 {
    525                   fprintf(stderr, _("ERROR: Command too long: %s\n"),
    526                           argv[num]);
    527                   return (EXIT_FAILURE);
    528                 }
    529               strncpy (message, argv[num], SH_MAXMSG);
    530               message[SH_MAXMSG-1] = '\0';
    531               strncat(message, ":", SH_MAXMSG-strlen(message)-1);
    532               message[SH_MAXMSG-1] = '\0';
    533               flag = 1;
    534             }
     559            checknull(argv[0], argv[num]);
     560            checklen(argv[0], argv[num], SH_MAXMSG-1);
     561            if (0 != check_command(argv[num]))
     562              print_usage_and_exit(argv[0], EXIT_FAILURE);
     563            safe_copy(message, argv[num], SH_MAXMSG);
     564            strncat(message, ":", SH_MAXMSG-strlen(message)-1);
     565            message[SH_MAXMSG-1] = '\0';
     566            flag = 1;
    535567            break;
    536 
    537568          default:
    538             usage(argv[0]);
    539             fprintf(stderr, _("ERROR: unknown option -%c\n"), argv[num][1]);
    540             return (EXIT_FAILURE);
     569            fprintf(stderr, _("ERROR: unknown option -%c\n\n"), argv[num][1]);
     570            print_usage_and_exit(argv[0], EXIT_FAILURE);
     571            break;
    541572        }
    542573      --argc; ++num;
     
    544575
    545576  if (flag == 0) /* no command given */
    546     {
    547       usage(argv[0]);
    548       return (EXIT_FAILURE);
    549     }
     577    print_usage_and_exit(argv[0], EXIT_FAILURE);
    550578
    551579  if (argc > 1)
    552580    {
    553       if (strlen(argv[num]) > (SH_MAXMSG - strlen(message) -1))
    554         {
    555           fprintf(stderr, _("ERROR: Hostname too long: %s\n"), argv[num]);
    556           return (EXIT_FAILURE);
    557         }
    558       strncat (message, argv[num], SH_MAXMSG -strlen(message) - 1);
     581      checklen(argv[0], argv[num], SH_MAXMSG - strlen(message) - 1);
     582      strncat (message, argv[num], SH_MAXMSG - strlen(message) - 1);
    559583      message[SH_MAXMSG-1] = '\0';
    560584    }
    561585  else
    562586    {
    563       if (message[0] == 'P' && message[1] == 'R' &&
    564           message[2] == 'O' && message[3] == 'B' && message[4] == 'E' )
     587      if (0 == strncmp(message, _("PROBE"), 5) ||
     588          0 == strncmp(message, _("LIST"),  4))
    565589        {
    566590          strncat (message, _("dummy"), SH_MAXMSG -strlen(message) - 1);
    567591          message[SH_MAXMSG-1] = '\0';
    568592        }
    569       else if (message[0] == 'L' && message[1] == 'I' &&
    570                message[2] == 'S' && message[3] == 'T')
    571         {
    572           strncat (message, _("dummy"), SH_MAXMSG -strlen(message) - 1);
    573           message[SH_MAXMSG-1] = '\0';
    574         }
    575593      else
    576594        {
    577595          fprintf(stderr, "%s", _("ERROR: this command requires a hostname\n"));
    578           usage(argv[0]);
    579           return (EXIT_FAILURE);
    580         }
    581     }
    582 
    583   fixup_message(message);
    584 
    585   /* OpenBSD wants >= 1024
    586    */
    587   if (NULL == getcwd(clientcd, 1024))
    588     {
    589       perror(_("ERROR: getcwd"));
    590       return (EXIT_FAILURE);
    591     }
    592   size = strlen(clientcd) + 1 + strlen(CLIENT) + 6;
    593   sockname = calloc(1,size);
    594   if (!sockname)
    595     {
    596       perror(_("ERROR: main: malloc"));
    597       return (EXIT_FAILURE);
    598     }
    599 #ifdef HAVE_VSNPRINTF
    600   snprintf(sockname, size, _("%s/%s.sock"), clientcd, CLIENT);
    601 #else
    602   sprintf(sockname, _("%s/%s.sock"), clientcd, CLIENT);
    603 #endif
     596          print_usage_and_exit(argv[0], EXIT_FAILURE);
     597        }
     598    }
     599
     600  if (fixup_message(message) < 0)
     601    return (EXIT_FAILURE);
    604602
    605603  /* Make the socket.
    606604   */
    607   sock = make_named_socket (sockname);
     605  sock = create_unix_socket ();
    608606  if (sock < 0)
    609     {
    610       return (EXIT_FAILURE);
    611     }
     607    return (EXIT_FAILURE);
    612608
    613609  /* Set up termination handler.
     
    630626  /* Wait for a reply.
    631627   */
    632   if (message[0] == 'L' && message[1] == 'I' &&
    633       message[2] == 'S' && message[3] == 'T')
    634     {
    635       if (verbose)
     628  if (verbose)
     629    {
     630      if (0 == strncmp(message, "LIST", 4))
    636631        fprintf(stdout, "%s", _("# Waiting for listing.\n"));
    637     }
    638   else
    639     {
    640       if (verbose)
     632      else
    641633        fprintf(stdout, "%s", _("# Waiting for confirmation.\n"));
    642634    }
     635
    643636  status = recv_from_server (message);
    644637
    645638  if (status < 0)
    646639    {
    647       fprintf(stderr, "%s", _("ERROR: receiving data from server failed.\n"));
     640      fputs(_("ERROR: unexpected or no reply from server.\n"), stderr);
    648641      (void) termination_handler(0);
    649642      return (EXIT_FAILURE);
Note: See TracChangeset for help on using the changeset viewer.