Print this page
    
NEX-16805 Add smbutil discon command
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/cmd/fs.d/smbclnt/smbiod-svc/smbiod-svc.c
          +++ new/usr/src/cmd/fs.d/smbclnt/smbiod-svc/smbiod-svc.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  24   24   */
  25   25  
  26   26  /*
  27   27   * SMBFS I/O Daemon (SMF service)
  28   28   */
  29   29  
  30   30  #include <sys/types.h>
  31   31  #include <sys/stat.h>
  32   32  #include <sys/note.h>
  33   33  #include <sys/queue.h>
  34   34  
  35   35  #include <errno.h>
  36   36  #include <fcntl.h>
  37   37  #include <signal.h>
  38   38  #include <stdarg.h>
  39   39  #include <stdio.h>
  40   40  #include <string.h>
  41   41  #include <strings.h>
  42   42  #include <stdlib.h>
  43   43  #include <synch.h>
  44   44  #include <time.h>
  45   45  #include <unistd.h>
  46   46  #include <ucred.h>
  47   47  #include <wait.h>
  48   48  #include <priv_utils.h>
  49   49  #include <err.h>
  50   50  #include <door.h>
  51   51  #include <libscf.h>
  52   52  #include <locale.h>
  53   53  #include <thread.h>
  54   54  #include <assert.h>
  55   55  
  56   56  #include <netsmb/smb_lib.h>
  57   57  
  58   58  static boolean_t d_flag = B_FALSE;
  59   59  
  60   60  /* Keep a list of child processes. */
  61   61  typedef struct _child {
  62   62          LIST_ENTRY(_child) list;
  63   63          pid_t pid;
  64   64          uid_t uid;
  65   65  } child_t;
  66   66  static LIST_HEAD(, _child) child_list = { 0 };
  67   67  mutex_t cl_mutex = DEFAULTMUTEX;
  68   68  
  69   69  static const char smbiod_path[] = "/usr/lib/smbfs/smbiod";
  70   70  static const char door_path[] = SMBIOD_SVC_DOOR;
  71   71  
  72   72  void svc_dispatch(void *cookie, char *argp, size_t argsz,
  73   73      door_desc_t *dp, uint_t n_desc);
  74   74  static int cmd_start(uid_t uid, gid_t gid);
  75   75  static int new_child(uid_t uid, gid_t gid);
  76   76  static void svc_sigchld(void);
  77   77  static void child_gone(uid_t, pid_t, int);
  78   78  static void svc_cleanup(void);
  79   79  
  80   80  static child_t *
  81   81  child_find_by_pid(pid_t pid)
  82   82  {
  83   83          child_t *cp;
  84   84  
  85   85          assert(MUTEX_HELD(&cl_mutex));
  86   86          LIST_FOREACH(cp, &child_list, list) {
  87   87                  if (cp->pid == pid)
  88   88                          return (cp);
  89   89          }
  90   90          return (NULL);
  91   91  }
  92   92  
  93   93  static child_t *
  94   94  child_find_by_uid(uid_t uid)
  95   95  {
  96   96          child_t *cp;
  97   97  
  98   98          assert(MUTEX_HELD(&cl_mutex));
  99   99          LIST_FOREACH(cp, &child_list, list) {
 100  100                  if (cp->uid == uid)
 101  101                          return (cp);
 102  102          }
 103  103          return (NULL);
 104  104  }
 105  105  
 106  106  /*
 107  107   * Find out if the service is already running.
 108  108   * Return: true, false.
 109  109   */
 110  110  static boolean_t
 111  111  already_running(void)
 112  112  {
 113  113          door_info_t info;
 114  114          int fd, rc;
 115  115  
 116  116          if ((fd = open(door_path, O_RDONLY)) < 0)
 117  117                  return (B_FALSE);
 118  118  
 119  119          rc = door_info(fd, &info);
 120  120          close(fd);
 121  121          if (rc < 0)
 122  122                  return (B_FALSE);
 123  123  
 124  124          return (B_TRUE);
 125  125  }
 126  126  
 127  127  /*
 128  128   * This function will fork off a child process,
 129  129   * from which only the child will return.
 130  130   *
 131  131   * The parent exit status is taken as the SMF start method
 132  132   * success or failure, so the parent waits (via pipe read)
 133  133   * for the child to finish initialization before it exits.
 134  134   * Use SMF error codes only on exit.
 135  135   */
 136  136  static int
 137  137  daemonize_init(void)
 138  138  {
 139  139          int pid, st;
 140  140          int pfds[2];
 141  141  
 142  142          chdir("/");
 143  143  
 144  144          if (pipe(pfds) < 0) {
 145  145                  perror("pipe");
 146  146                  exit(SMF_EXIT_ERR_FATAL);
 147  147          }
 148  148          if ((pid = fork1()) == -1) {
 149  149                  perror("fork");
 150  150                  exit(SMF_EXIT_ERR_FATAL);
 151  151          }
 152  152  
 153  153          /*
 154  154           * If we're the parent process, wait for either the child to send us
 155  155           * the appropriate exit status over the pipe or for the read to fail
 156  156           * (presumably with 0 for EOF if our child terminated abnormally).
 157  157           * If the read fails, exit with either the child's exit status if it
 158  158           * exited or with SMF_EXIT_ERR_FATAL if it died from a fatal signal.
 159  159           */
 160  160          if (pid != 0) {
 161  161                  /* parent */
 162  162                  close(pfds[1]);
 163  163                  if (read(pfds[0], &st, sizeof (st)) == sizeof (st))
 164  164                          _exit(st);
 165  165                  if (waitpid(pid, &st, 0) == pid && WIFEXITED(st))
 166  166                          _exit(WEXITSTATUS(st));
 167  167                  _exit(SMF_EXIT_ERR_FATAL);
 168  168          }
 169  169  
 170  170          /* child */
 171  171          close(pfds[0]);
 172  172  
 173  173          return (pfds[1]);
 174  174  }
 175  175  
 176  176  static void
 177  177  daemonize_fini(int pfd, int rc)
 178  178  {
 179  179          /* Tell parent we're ready. */
 180  180          (void) write(pfd, &rc, sizeof (rc));
 181  181          close(pfd);
 182  182  }
 183  183  
 184  184  int
 185  185  main(int argc, char **argv)
 186  186  {
 187  187          sigset_t oldmask, tmpmask;
 188  188          struct sigaction sa;
 189  189          struct rlimit rl;
 190  190          int door_fd = -1, tmp_fd = -1, pfd = -1;
 191  191          int c, sig;
 192  192          int rc = SMF_EXIT_ERR_FATAL;
 193  193          boolean_t created = B_FALSE, attached = B_FALSE;
 194  194  
 195  195          /* set locale and text domain for i18n */
 196  196          (void) setlocale(LC_ALL, "");
 197  197          (void) textdomain(TEXT_DOMAIN);
 198  198  
 199  199          while ((c = getopt(argc, argv, "d")) != -1) {
 200  200                  switch (c) {
 201  201                  case 'd':
 202  202                          /* Do debug messages. */
 203  203                          d_flag = B_TRUE;
 204  204                          break;
 205  205                  default:
 206  206                          fprintf(stderr, "Usage: %s [-d]\n", argv[0]);
 207  207                          return (SMF_EXIT_ERR_CONFIG);
 208  208                  }
 209  209          }
 210  210  
 211  211          if (already_running()) {
 212  212                  fprintf(stderr, "%s: already running", argv[0]);
 213  213                  return (rc);
 214  214          }
 215  215  
 216  216          /*
 217  217           * Raise the fd limit to max
 218  218           * errors here are non-fatal
 219  219           */
 220  220          if (getrlimit(RLIMIT_NOFILE, &rl) != 0) {
 221  221                  fprintf(stderr, "getrlimit failed, err %d\n", errno);
 222  222          } else if (rl.rlim_cur < rl.rlim_max) {
 223  223                  rl.rlim_cur = rl.rlim_max;
 224  224                  if (setrlimit(RLIMIT_NOFILE, &rl) != 0)
 225  225                          fprintf(stderr, "setrlimit "
 226  226                              "RLIMIT_NOFILE %d, err %d",
 227  227                              (int)rl.rlim_cur, errno);
 228  228          }
 229  229  
 230  230          /*
 231  231           * Want all signals blocked, as we're doing
 232  232           * synchronous delivery via sigwait below.
 233  233           */
 234  234          sigfillset(&tmpmask);
 235  235          sigprocmask(SIG_BLOCK, &tmpmask, &oldmask);
 236  236  
 237  237          /*
 238  238           * Do want SIGCHLD, and will waitpid().
 239  239           */
 240  240          sa.sa_flags = SA_NOCLDSTOP;
 241  241          sa.sa_handler = SIG_DFL;
 242  242          sigemptyset(&sa.sa_mask);
 243  243          sigaction(SIGCHLD, &sa, NULL);
 244  244  
 245  245          /*
 246  246           * Daemonize, unless debugging.
 247  247           */
 248  248          if (d_flag) {
 249  249                  /* debug: run in foregound (not a service) */
 250  250                  putenv("SMBFS_DEBUG=1");
 251  251          } else {
 252  252                  /* Non-debug: start daemon in the background. */
 253  253                  pfd = daemonize_init();
 254  254          }
 255  255  
 256  256          /*
 257  257           * Create directory for all smbiod doors.
 258  258           */
 259  259          if ((mkdir(SMBIOD_RUNDIR, 0755) < 0) && errno != EEXIST) {
 260  260                  perror(SMBIOD_RUNDIR);
 261  261                  goto out;
 262  262          }
 263  263  
 264  264          /*
 265  265           * Create a file for the main service door.
 266  266           */
 267  267          unlink(door_path);
 268  268          tmp_fd = open(door_path, O_RDWR|O_CREAT|O_EXCL, 0644);
 269  269          if (tmp_fd < 0) {
 270  270                  perror(door_path);
 271  271                  goto out;
 272  272          }
 273  273          close(tmp_fd);
 274  274          tmp_fd = -1;
 275  275          created = B_TRUE;
 276  276  
 277  277          /* Setup the door service. */
 278  278          door_fd = door_create(svc_dispatch, NULL,
 279  279              DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
 280  280          if (door_fd == -1) {
 281  281                  perror("svc door_create");
 282  282                  goto out;
 283  283          }
 284  284          fdetach(door_path);
 285  285          if (fattach(door_fd, door_path) < 0) {
 286  286                  fprintf(stderr, "%s: fattach failed, %s\n",
 287  287                      door_path, strerror(errno));
 288  288                  goto out;
 289  289          }
 290  290          attached = B_TRUE;
 291  291  
 292  292          /*
 293  293           * Initializations done.  Tell start method we're up.
 294  294           */
 295  295          rc = SMF_EXIT_OK;
 296  296          if (pfd != -1) {
 297  297                  daemonize_fini(pfd, rc);
 298  298                  pfd = -1;
 299  299          }
 300  300  
 301  301          /*
 302  302           * Main thread just waits for signals.
 303  303           */
 304  304  again:
 305  305          sig = sigwait(&tmpmask);
 306  306          if (d_flag)
 307  307                  fprintf(stderr, "main: sig=%d\n", sig);
 308  308          switch (sig) {
 309  309          case SIGINT:
 310  310          case SIGTERM:
 311  311                  /*
 312  312                   * The whole process contract gets a SIGTERM
 313  313                   * at once.  Give children a chance to exit
 314  314                   * so we can do normal SIGCHLD cleanup.
 315  315                   * Prevent new door_open calls.
 316  316                   */
 317  317                  fdetach(door_path);
 318  318                  attached = B_FALSE;
 319  319                  alarm(2);
 320  320                  goto again;
 321  321          case SIGALRM:
 322  322                  break;  /* normal termination */
 323  323          case SIGCHLD:
 324  324                  svc_sigchld();
 325  325                  goto again;
 326  326          case SIGCONT:
 327  327                  goto again;
 328  328          default:
 329  329                  /* Unexpected signal. */
 330  330                  fprintf(stderr, "svc_main: unexpected sig=%d\n", sig);
 331  331                  break;
 332  332          }
 333  333  
 334  334  out:
 335  335          if (attached)
 336  336                  fdetach(door_path);
 337  337          if (door_fd != -1)
 338  338                  door_revoke(door_fd);
 339  339          if (created)
 340  340                  unlink(door_path);
 341  341  
 342  342          /* NB: door threads gone now. */
 343  343          svc_cleanup();
 344  344  
 345  345          /* If startup error, report to parent. */
 346  346          if (pfd != -1)
 347  347                  daemonize_fini(pfd, rc);
 348  348  
 349  349          return (rc);
 350  350  }
 351  351  
 352  352  /*ARGSUSED*/
 353  353  void
 354  354  svc_dispatch(void *cookie, char *argp, size_t argsz,
 355  355      door_desc_t *dp, uint_t n_desc)
 356  356  {
 357  357          ucred_t *ucred = NULL;
 358  358          uid_t uid;
 359  359          gid_t gid;
 360  360          int32_t cmd, rc;
 361  361  
 362  362          /*
 363  363           * Allow a NULL arg call to check if this
 364  364           * daemon is running.  Just return zero.
 365  365           */
 366  366          if (argp == NULL) {
 367  367                  rc = 0;
 368  368                  goto out;
 369  369          }
 370  370  
 371  371          /*
 372  372           * Get the caller's credentials.
 373  373           * (from client side of door)
 374  374           */
 375  375          if (door_ucred(&ucred) != 0) {
 376  376                  rc = EACCES;
 377  377                  goto out;
 378  378          }
 379  379          uid = ucred_getruid(ucred);
 380  380          gid = ucred_getrgid(ucred);
 381  381  
 382  382          /*
 383  383           * Arg is just an int command code.
 384  384           * Reply is also an int.
 385  385           */
 386  386          if (argsz != sizeof (cmd)) {
 387  387                  rc = EINVAL;
 388  388                  goto out;
 389  389          }
 390  390          bcopy(argp, &cmd, sizeof (cmd));
 391  391          switch (cmd) {
 392  392          case SMBIOD_START:
 393  393                  rc = cmd_start(uid, gid);
 394  394                  break;
 395  395          default:
 396  396                  rc = EINVAL;
 397  397                  goto out;
 398  398          }
 399  399  
 400  400  out:
 401  401          if (ucred != NULL)
 402  402                  ucred_free(ucred);
 403  403  
 404  404          door_return((void *)&rc, sizeof (rc), NULL, 0);
 405  405  }
 406  406  
 407  407  /*
 408  408   * Start a per-user smbiod, if not already running.
 409  409   */
 410  410  int
 411  411  cmd_start(uid_t uid, gid_t gid)
 412  412  {
 413  413          char door_file[64];
 414  414          child_t *cp;
 415  415          int pid, fd = -1;
 416  416  
 417  417          mutex_lock(&cl_mutex);
 418  418          cp = child_find_by_uid(uid);
 419  419          if (cp != NULL) {
 420  420                  /* This UID already has an IOD. */
 421  421                  mutex_unlock(&cl_mutex);
 422  422                  if (d_flag) {
 423  423                          fprintf(stderr, "cmd_start: uid %d"
 424  424                              " already has an iod\n", uid);
 425  425                  }
 426  426                  return (0);
 427  427          }
 428  428  
 429  429          /*
 430  430           * OK, create a new child.
 431  431           */
 432  432          cp = malloc(sizeof (*cp));
 433  433          if (cp == NULL) {
 434  434                  mutex_unlock(&cl_mutex);
 435  435                  return (ENOMEM);
 436  436          }
 437  437          cp->pid = 0; /* update below */
 438  438          cp->uid = uid;
 439  439          LIST_INSERT_HEAD(&child_list, cp, list);
 440  440          mutex_unlock(&cl_mutex);
 441  441  
 442  442          /*
 443  443           * The child will not have permission to create or
 444  444           * destroy files in SMBIOD_RUNDIR so do that here.
 445  445           */
 446  446          snprintf(door_file, sizeof (door_file),
 447  447              SMBIOD_USR_DOOR, cp->uid);
 448  448          unlink(door_file);
 449  449          fd = open(door_file, O_RDWR|O_CREAT|O_EXCL, 0600);
 450  450          if (fd < 0) {
 451  451                  perror(door_file);
 452  452                  goto errout;
 453  453          }
 454  454          if (fchown(fd, uid, gid) < 0) {
 455  455                  perror(door_file);
 456  456                  goto errout;
 457  457          }
 458  458          close(fd);
 459  459          fd = -1;
 460  460  
 461  461          if ((pid = fork1()) == -1) {
 462  462                  perror("fork");
 463  463                  goto errout;
 464  464          }
 465  465          if (pid == 0) {
 466  466                  (void) new_child(uid, gid);
 467  467                  _exit(1);
 468  468          }
 469  469          /* parent */
 470  470          cp->pid = pid;
 471  471  
 472  472          if (d_flag) {
 473  473                  fprintf(stderr, "cmd_start: uid %d new iod, pid %d\n",
 474  474                      uid, pid);
 475  475          }
 476  476  
 477  477          return (0);
 478  478  
 479  479  errout:
 480  480          if (fd != -1)
 481  481                  close(fd);
 482  482          mutex_lock(&cl_mutex);
 483  483          LIST_REMOVE(cp, list);
 484  484          mutex_unlock(&cl_mutex);
 485  485          free(cp);
 486  486          return (errno);
 487  487  }
 488  488  
 489  489  /*
 490  490   * Assume the passed credentials (from the door client),
 491  491   * drop any extra privileges, and exec the per-user iod.
 492  492   */
 493  493  static int
 494  494  new_child(uid_t uid, gid_t gid)
 495  495  {
 496  496          char *argv[2];
 497  497          int flags, rc;
 498  498  
 499  499          flags = PU_RESETGROUPS | PU_LIMITPRIVS | PU_INHERITPRIVS;
 500  500          rc = __init_daemon_priv(flags, uid, gid, PRIV_NET_ACCESS, NULL);
 501  501          if (rc != 0)
 502  502                  return (errno);
 503  503  
 504  504          argv[0] = "smbiod";
 505  505          argv[1] = NULL;
 506  506          (void) execv(smbiod_path, argv);
 507  507          return (errno);
 508  508  }
 509  509  
 510  510  static void
 511  511  svc_sigchld(void)
 512  512  {
 513  513          child_t *cp;
 514  514          pid_t pid;
 515  515          int err, status, found = 0;
 516  516  
 517  517          mutex_lock(&cl_mutex);
 518  518  
 519  519          while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
 520  520  
 521  521                  found++;
 522  522                  if (d_flag)
 523  523                          fprintf(stderr, "svc_sigchld: pid %d\n", (int)pid);
 524  524  
 525  525                  cp = child_find_by_pid(pid);
 526  526                  if (cp == NULL) {
 527  527                          fprintf(stderr, "Unknown pid %d\n", (int)pid);
 528  528                          continue;
 529  529                  }
 530  530                  child_gone(cp->uid, cp->pid, status);
 531  531                  LIST_REMOVE(cp, list);
 532  532                  free(cp);
 533  533          }
 534  534          err = errno;
 535  535  
 536  536          mutex_unlock(&cl_mutex);
 537  537  
 538  538          /* ECHILD is the normal end of loop. */
 539  539          if (pid < 0 && err != ECHILD)
 540  540                  fprintf(stderr, "svc_sigchld: waitpid err %d\n", err);
 541  541          if (found == 0)
 542  542                  fprintf(stderr, "svc_sigchld: no children?\n");
 543  543  }
 544  544  
 545  545  static void
 546  546  child_gone(uid_t uid, pid_t pid, int status)
 547  547  {
 548  548          char door_file[64];
 549  549          int x;
 550  550  
 551  551          if (d_flag)
 552  552                  fprintf(stderr, "child_gone: uid %d pid %d\n",
  
    | 
      ↓ open down ↓ | 
    552 lines elided | 
    
      ↑ open up ↑ | 
  
 553  553                      uid, (int)pid);
 554  554  
 555  555          snprintf(door_file, sizeof (door_file),
 556  556              SMBIOD_RUNDIR "/%d", uid);
 557  557          unlink(door_file);
 558  558  
 559  559          if (WIFEXITED(status)) {
 560  560                  x = WEXITSTATUS(status);
 561  561                  if (x != 0) {
 562  562                          fprintf(stderr,
 563      -                            "uid %d, pid %d exit %d",
      563 +                            "uid %d, pid %d exit %d\n",
 564  564                              uid, (int)pid, x);
 565  565                  }
 566  566          }
 567  567          if (WIFSIGNALED(status)) {
 568  568                  x = WTERMSIG(status);
 569  569                  fprintf(stderr,
 570      -                    "uid %d, pid %d signal %d",
      570 +                    "uid %d, pid %d signal %d\n",
 571  571                      uid, (int)pid, x);
 572  572          }
 573  573  }
 574  574  
 575  575  /*
 576  576   * Final cleanup before exit.  Unlink child doors, etc.
 577  577   * Called while single threaded, so no locks needed here.
 578  578   * The list is normally empty by now due to svc_sigchld
 579  579   * calls during shutdown.  But in case there were any
 580  580   * straglers, do cleanup here.  Don't bother freeing any
 581  581   * list elements here, as we're exiting.
 582  582   */
 583  583  static void
 584  584  svc_cleanup(void)
 585  585  {
 586  586          child_t *cp;
 587  587  
 588  588          LIST_FOREACH(cp, &child_list, list) {
 589  589                  child_gone(cp->uid, cp->pid, 0);
 590  590          }
 591  591  }
  
    | 
      ↓ open down ↓ | 
    11 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX