1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #pragma ident   "%Z%%M% %I%     %E% SMI"
  27 
  28 /*
  29  * Main routines for cachefs daemon.
  30  */
  31 
  32 #include <stdio.h>
  33 #include <stdio_ext.h>
  34 #include <stdlib.h>
  35 #include <string.h>
  36 #include <errno.h>
  37 #include <rpc/rpc.h>
  38 #include <rpc/pmap_clnt.h> /* for pmap_unset */
  39 #include <string.h> /* strcmp */
  40 #include <signal.h>
  41 #include <unistd.h> /* setsid */
  42 #include <sys/types.h>
  43 #include <memory.h>
  44 #include <stropts.h>
  45 #include <netconfig.h>
  46 #include <libintl.h>
  47 #include <locale.h>
  48 #include <thread.h>
  49 #include <sys/resource.h> /* rlimit */
  50 #include <synch.h>
  51 #include <mdbug/mdbug.h>
  52 #include <common/cachefsd.h>
  53 #include <sys/fs/cachefs_fs.h>
  54 #include <sys/fs/cachefs_dlog.h>
  55 #include <sys/fs/cachefs_ioctl.h>
  56 #include "cfsd.h"
  57 #include "cfsd_kmod.h"
  58 #include "cfsd_maptbl.h"
  59 #include "cfsd_logfile.h"
  60 #include "cfsd_fscache.h"
  61 #include "cfsd_cache.h"
  62 #include "cfsd_all.h"
  63 #include "cfsd_subr.h"
  64 
  65 #define RPCGEN_ACTION(X) X
  66 #include "cachefsd_tbl.i"
  67 
  68 #ifndef SIG_PF
  69 #define SIG_PF void(*)(int)
  70 #endif
  71 
  72 typedef bool_t (* LOCAL)(void *, void *, struct svc_req *);
  73 
  74 /* global definitions */
  75 cfsd_all_object_t *all_object_p = NULL;
  76 
  77 /* forward references */
  78 void msgout(char *msgp);
  79 void cachefsdprog_1(struct svc_req *rqstp, register SVCXPRT *transp);
  80 void sigusr1_handler(int, siginfo_t *, void *);
  81 static int void_close(void *, int);
  82 
  83 /*
  84  * -----------------------------------------------------------------
  85  *                      main
  86  *
  87  * Description:
  88  *      main routine for the chart daemon.
  89  * Arguments:
  90  *      argc
  91  *      argv
  92  * Returns:
  93  *      Returns 0 for a normal exit, !0 if an error occurred.
  94  * Preconditions:
  95  *      precond(argv)
  96  */
  97 
  98 int
  99 main(int argc, char **argv)
 100 {
 101         pid_t pid;
 102         int xx;
 103         char mname[FMNAMESZ + 1];
 104         int opt_fork = 0;
 105         int opt_mt = 0;
 106         char *opt_root = NULL;
 107         int c;
 108         char *msgp;
 109         int size;
 110         struct rlimit rl;
 111         struct sigaction nact;
 112         int ofd = -1;
 113         char *netid;
 114         struct netconfig *nconf = NULL;
 115         SVCXPRT *transp;
 116         cfsd_fscache_object_t *fscache_object_p;
 117         int mode;
 118         /* selectable maximum RPC request record size */
 119         int maxrecsz = RPC_MAXDATASIZE;
 120 
 121         dbug_enter("main");
 122         dbug_process("cfsadmin");
 123 
 124         (void) setlocale(LC_ALL, "");
 125 #if !defined(TEXT_DOMAIN)
 126 #define TEXT_DOMAIN     "SYS_TEST"
 127 #endif
 128         (void) textdomain(TEXT_DOMAIN);
 129 
 130         /* verify root */
 131         if (getuid() != 0) {
 132                 fprintf(stderr, gettext("%s: must be run by root\n"), argv[0]);
 133                 dbug_leave("main");
 134                 return (1);
 135         }
 136 
 137         /* Increase number of file descriptors to maximum allowable */
 138         xx = getrlimit(RLIMIT_NOFILE, &rl);
 139         if (xx < 0) {
 140                 dbug_print(("error",
 141                     "getrlimit/RLIMIT_NOFILE failed %d", errno));
 142                 dbug_leave("main");
 143                 return (1);
 144         }
 145         rl.rlim_cur = rl.rlim_max;
 146         xx = setrlimit(RLIMIT_NOFILE, &rl);
 147         if (xx < 0) {
 148                 dbug_print(("error",
 149                     "setrlimit/RLIMIT_NOFILE failed %d", errno));
 150                 dbug_leave("main");
 151                 return (1);
 152         }
 153         (void) enable_extended_FILE_stdio(-1, -1);
 154 
 155         while ((c = getopt(argc, argv, "fmr:#:")) != EOF) {
 156                 switch (c) {
 157                 case 'f':
 158                         opt_fork = 1;
 159                         break;
 160 
 161                 case 'm':
 162                         /*
 163                          * XXX don't use this until race between mount
 164                          * and umount is fixed.
 165                          */
 166                         opt_mt = 1;
 167                         break;
 168 
 169                 case 'r':
 170                         opt_root = optarg;
 171                         break;
 172 
 173                 case '#':       /* dbug args */
 174                         msgp = dbug_push(optarg);
 175                         if (msgp) {
 176                                 printf("dbug_push failed \"%s\"\n", msgp);
 177                                 dbug_leave("main");
 178                                 return (1);
 179                         }
 180                         ofd = db_getfd();
 181                         break;
 182 
 183                 default:
 184                         printf(gettext("illegal switch\n"));
 185                         dbug_leave("main");
 186                         return (1);
 187                 }
 188         }
 189 
 190         /* XXX need some way to prevent multiple daemons from running */
 191 
 192         dbug_print(("info", "cachefsd started..."));
 193 
 194         if (opt_mt) {
 195                 dbug_print(("info", "MT_AUTO mode set"));
 196                 mode = RPC_SVC_MT_AUTO;
 197                 if (!rpc_control(RPC_SVC_MTMODE_SET, &mode)) {
 198                         msgout(gettext("unable to set automatic MT mode."));
 199                         dbug_leave("main");
 200                         return (1);
 201                 }
 202         }
 203 
 204         /*
 205          * Enable non-blocking mode and maximum record size checks for
 206          * connection oriented transports.
 207          */
 208         if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrecsz)) {
 209                 msgout(gettext("unable to set max RPC record size"));
 210         }
 211 
 212         /* ignore sigpipe */
 213         nact.sa_handler = SIG_IGN;
 214         nact.sa_sigaction = NULL;
 215         sigemptyset(&nact.sa_mask);
 216         nact.sa_flags = 0;
 217         xx = sigaction(SIGPIPE, &nact, NULL);
 218         if (xx) {
 219                 dbug_print(("error", "sigaction/SIGPIPE failed %d", errno));
 220         }
 221 
 222         /* catch sigusr1 signals, used to wake up threads */
 223         nact.sa_handler = NULL;
 224         nact.sa_sigaction = sigusr1_handler;
 225         sigemptyset(&nact.sa_mask);
 226         nact.sa_flags = SA_SIGINFO;
 227         xx = sigaction(SIGUSR1, &nact, NULL);
 228         if (xx) {
 229                 dbug_print(("error", "sigaction failed %d", errno));
 230         }
 231 
 232         /* do not set up rpc services if just taking care of root */
 233         if (opt_root) {
 234                 dbug_print(("info", "handling just root"));
 235 
 236                 /* make the fscache object */
 237                 fscache_object_p =
 238                     cfsd_fscache_create("rootcache", opt_root, 1);
 239 
 240                 /* init the fscache object with mount information */
 241                 fscache_lock(fscache_object_p);
 242                 fscache_object_p->i_refcnt++;
 243                 fscache_setup(fscache_object_p);
 244                 fscache_object_p->i_mounted = 1;
 245                 fscache_unlock(fscache_object_p);
 246 
 247                 if (fscache_object_p->i_disconnectable &&
 248                     fscache_object_p->i_mounted) {
 249                         pid = fork();
 250                         if (pid < 0) {
 251                                 perror(gettext("cannot fork"));
 252                                 cfsd_fscache_destroy(fscache_object_p);
 253                                 dbug_leave("main");
 254                                 return (1);
 255                         }
 256                         if (pid) {
 257                                 cfsd_fscache_destroy(fscache_object_p);
 258                                 dbug_leave("main");
 259                                 return (0);
 260                         }
 261                         (void) fdwalk(void_close, &ofd);
 262                         xx = open("/dev/sysmsg", O_RDWR);
 263                         (void) dup2(xx, 1);
 264                         (void) dup2(xx, 2);
 265                         setsid();
 266 
 267                         fscache_process(fscache_object_p);
 268                 } else {
 269                         /* not disconnectable */
 270                         cfsd_fscache_destroy(fscache_object_p);
 271                         dbug_leave("main");
 272                         return (1);
 273                 }
 274                 cfsd_fscache_destroy(fscache_object_p);
 275                 dbug_leave("main");
 276                 return (0);
 277         }
 278 
 279         /* if a inetd started us */
 280         else if (!ioctl(0, I_LOOK, mname) &&
 281                 ((strcmp(mname, "sockmod") == 0) ||
 282                 (strcmp(mname, "timod") == 0))) {
 283                 dbug_print(("info", "started by inetd"));
 284 
 285                 if (freopen("/dev/null", "w", stderr) == NULL)
 286                         return (1);
 287 
 288                 /* started from inetd */
 289                 if ((netid = getenv("NLSPROVIDER")) == NULL)
 290                         netid = "ticotsord";
 291                 if ((nconf = getnetconfigent(netid)) == NULL)
 292                         msgout(gettext("cannot get transport info"));
 293 
 294                 if (strcmp(mname, "sockmod") == 0) {
 295                         if (ioctl(0, I_POP, 0) || ioctl(0, I_PUSH, "timod")) {
 296                                 msgout(
 297                                     gettext("could not get the right module"));
 298                                 dbug_leave("main");
 299                                 return (1);
 300                         }
 301                 }
 302                 if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) {
 303                         msgout(gettext("cannot create server handle"));
 304                         dbug_leave("main");
 305                         return (1);
 306                 }
 307                 if (nconf)
 308                         freenetconfigent(nconf);
 309                 xx = svc_reg(transp, CACHEFSDPROG, CACHEFSDVERS,
 310                         cachefsdprog_1, 0);
 311                 if (!xx) {
 312                         msgout(gettext(
 313                             "unable to reg (CACHEFSDPROG, CACHEFSDVERS)."));
 314                         dbug_leave("main");
 315                         return (1);
 316                 }
 317         }
 318 
 319         /* else if started by hand */
 320         else {
 321                 /* if we should fork */
 322                 if (opt_fork) {
 323                         dbug_print(("info", "forking"));
 324                         pid = fork();
 325                         if (pid < 0) {
 326                                 perror(gettext("cannot fork"));
 327                                 dbug_leave("main");
 328                                 return (1);
 329                         }
 330                         if (pid) {
 331                                 dbug_leave("main");
 332                                 return (0);
 333                         }
 334                         (void) fdwalk(void_close, &ofd);
 335                         xx = open("/dev/sysmsg", 2);
 336                         (void) dup2(xx, 1);
 337                         (void) dup2(xx, 2);
 338                         setsid();
 339                 }
 340 
 341                 /* connect to *ANY* local loopback transport provider */
 342                 xx = svc_create(cachefsdprog_1, CACHEFSDPROG, CACHEFSDVERS,
 343                     "local");
 344                 if (!xx) {
 345                         msgout(gettext("unable to create (CACHEFSDPROG, "
 346                                 "CACHEFSDVERS) for netpath."));
 347                         dbug_leave("main");
 348                         return (1);
 349                 }
 350         }
 351 
 352         /* find existing caches and mounted file systems */
 353         all_object_p = cfsd_all_create();
 354         subr_cache_setup(all_object_p);
 355 
 356         /* process requests */
 357         svc_run();
 358 
 359         msgout(gettext("svc_run returned"));
 360         cfsd_all_destroy(all_object_p);
 361         dbug_leave("main");
 362         return (1);
 363 }
 364 
 365 /*
 366  * Callback function for fdwalk() to close all files.
 367  */
 368 static int
 369 void_close(void *ofdp, int fd)
 370 {
 371         if (fd != *(int *)ofdp) {
 372                 if (close(fd) != 0)
 373                         dbug_print(("err",
 374                             "cannot close fd %d, %d", fd, errno));
 375         }
 376         return (0);
 377 }
 378 
 379 /*
 380  * -----------------------------------------------------------------
 381  *                      msgout
 382  *
 383  * Description:
 384  *      Outputs an error message to stderr.
 385  * Arguments:
 386  *      msgp
 387  * Returns:
 388  * Preconditions:
 389  *      precond(msgp)
 390  */
 391 
 392 void
 393 msgout(char *msgp)
 394 {
 395         dbug_enter("msgout");
 396         dbug_precond(msgp);
 397 
 398         (void) fprintf(stderr, "%s\n", msgp);
 399         dbug_leave("msgout");
 400 }
 401 
 402 
 403 /*
 404  * -----------------------------------------------------------------
 405  *                      cachefsdprog_1
 406  *
 407  * Description:
 408  * Arguments:
 409  *      rqstp
 410  *      transp
 411  * Returns:
 412  * Preconditions:
 413  *      precond(rqstp)
 414  *      precond(transp)
 415  */
 416 void
 417 cachefsdprog_1(struct svc_req *rqstp, register SVCXPRT *transp)
 418 {
 419         int index;
 420         struct rpcgen_table *rtp;
 421         void *argumentp = NULL;
 422         void *resultp = NULL;
 423         LOCAL local;
 424         int xx;
 425 
 426         dbug_enter("cachefsdprog_1");
 427 
 428         dbug_precond(rqstp);
 429         dbug_precond(transp);
 430 
 431         /* make sure a valid command number */
 432         index = rqstp->rq_proc;
 433         if ((index < 0) || (cachefsdprog_1_nproc <= index)) {
 434                 msgout(gettext("bad message"));
 435                 svcerr_noproc(transp);
 436                 dbug_leave("cachefsdprog_1");
 437                 return;
 438         }
 439 
 440         /* get command information */
 441         rtp = &cachefsdprog_1_table[index];
 442 
 443         /* get memory for the arguments */
 444         if (rtp->len_arg != 0)
 445                 argumentp = (void *)cfsd_calloc(rtp->len_arg);
 446 
 447         /* get memory for the results */
 448         if (rtp->len_res != 0)
 449                 resultp = (void *)cfsd_calloc(rtp->len_res);
 450 
 451         /* get the arguments */
 452         if (rtp->xdr_arg && argumentp) {
 453                 if (!svc_getargs(transp, rtp->xdr_arg, (caddr_t)argumentp)) {
 454                         svcerr_decode(transp);
 455                         cfsd_free(argumentp);
 456                         cfsd_free(resultp);
 457                         dbug_leave("cachefsdprog_1");
 458                         return;
 459                 }
 460         }
 461 
 462         /* call the routine to process the command */
 463         local = (LOCAL)rtp->proc;
 464         xx = (*local)(argumentp, resultp, rqstp);
 465 
 466         /* if the command could not be processed */
 467         if (xx == 0) {
 468                 svcerr_systemerr(transp);
 469         }
 470 
 471         /* else send the results back to the caller */
 472         else {
 473                 xx = svc_sendreply(transp, rtp->xdr_res, (caddr_t)resultp);
 474                 if (!xx)
 475                         svcerr_systemerr(transp);
 476 
 477                 /* free the results */
 478                 xx = cachefsdprog_1_freeresult(transp, rtp->xdr_res,
 479                     (caddr_t)resultp);
 480                 if (xx == 0)
 481                         msgout(gettext("unable to free results"));
 482         }
 483 
 484         /* free the passed in arguments */
 485         if (!svc_freeargs(transp, rtp->xdr_arg, (caddr_t)argumentp)) {
 486                 msgout(gettext("unable to free arguments"));
 487                 abort();
 488         }
 489 
 490         if (argumentp)
 491                 cfsd_free(argumentp);
 492         if (resultp)
 493                 cfsd_free(resultp);
 494         dbug_leave("cachefsdprog_1");
 495 }
 496 
 497 /*
 498  *                      sigusr1_handler
 499  *
 500  * Description:
 501  *      Catches sigusr1 signal so threads wake up.
 502  * Arguments:
 503  * Returns:
 504  * Preconditions:
 505  */
 506 void
 507 sigusr1_handler(int sig, siginfo_t *sp, void *vp)
 508 {
 509 }