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, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * -----------------------------------------------------------------
  24  *
  25  *                      umount.c
  26  *
  27  * CFS specific umount command.
  28  */
  29 
  30 #pragma ident   "%Z%%M% %I%     %E% SMI"
  31 
  32 /*
  33  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
  34  * Use is subject to license terms.
  35  */
  36 
  37 #include <locale.h>
  38 #include <stdio.h>
  39 #include <stdlib.h>
  40 #include <string.h>
  41 #include <stdarg.h>
  42 #include <unistd.h>
  43 #include <limits.h>
  44 #include <errno.h>
  45 #include <wait.h>
  46 #include <ctype.h>
  47 #include <fcntl.h>
  48 #include <signal.h>
  49 #include <sys/types.h>
  50 #include <sys/param.h>
  51 #include <sys/stat.h>
  52 #include <sys/fcntl.h>
  53 #include <sys/mount.h>
  54 #include <sys/mntent.h>
  55 #include <sys/mnttab.h>
  56 #include <sys/fs/cachefs_fs.h>
  57 #include <fslib.h>
  58 #include <sys/utsname.h>
  59 #include <rpc/rpc.h>
  60 #include "../common/subr.h"
  61 #include "../common/cachefsd.h"
  62 
  63 /* forward references */
  64 void pr_err(char *fmt, ...);
  65 void usage(char *msgp);
  66 int daemon_unmount(char *mntptp, int);
  67 
  68 /*
  69  *
  70  *                      main
  71  *
  72  * Description:
  73  *      Main routine for the cfs umount program.
  74  * Arguments:
  75  *      argc    number of command line arguments
  76  *      argv    list of command line arguments
  77  * Returns:
  78  *      Returns 0 for success or 1 if an error occurs.
  79  * Preconditions:
  80  */
  81 
  82 int
  83 main(int argc, char **argv)
  84 {
  85         char *strp;
  86         int xx;
  87         int ret;
  88         struct mnttab mget;
  89         struct mnttab mref;
  90         FILE *finp;
  91         char mnt_front[PATH_MAX];
  92         char mnt_back[PATH_MAX];
  93         char *p, mnt_frontns[PATH_MAX];
  94         pid_t pid;
  95         int stat_loc;
  96         char *cachedirp, *s;
  97         int ufd, c;
  98         int flag = 0;
  99 
 100         (void) setlocale(LC_ALL, "");
 101 #if !defined(TEXT_DOMAIN)
 102 #define TEXT_DOMAIN     "SYS_TEST"
 103 #endif
 104         (void) textdomain(TEXT_DOMAIN);
 105 
 106         /*
 107          * Set options
 108          */
 109         while ((c = getopt(argc, argv, "f")) != EOF) {
 110                 switch (c) {
 111                 case 'f':
 112                         flag |= MS_FORCE; /* forced unmount is desired */
 113                         break;
 114                 default:
 115                         usage("Incorrect number of arguments specified");
 116                         return (1);
 117                 }
 118         }
 119         if (flag & MS_FORCE)
 120                 strcpy(mnt_front, argv[2]);
 121         else
 122                 strcpy(mnt_front, argv[1]);
 123         mnt_back[0] = '\0';
 124 
 125         if (strlen(argv[1]) >= (size_t)PATH_MAX) {
 126                 pr_err(gettext("name too long"));
 127                 return (1);
 128         }
 129 
 130         /*
 131          * An unmount from autofs may have a
 132          * space appended to the path.
 133          * Trim it off before attempting to
 134          * match the mountpoint in mnttab
 135          */
 136         strcpy(mnt_frontns, mnt_front);
 137         p = &mnt_frontns[strlen(mnt_frontns) - 1];
 138         if (*p == ' ')
 139                 *p = '\0';
 140 
 141         /* get the mount point and the back file system mount point */
 142         finp = fopen(MNTTAB, "r");
 143         if (finp) {
 144                 mntnull(&mref);
 145                 mref.mnt_mountp = mnt_frontns;
 146                 mref.mnt_fstype = "cachefs";
 147                 ret = getmntany(finp, &mget, &mref);
 148 
 149                 cachedirp = (char *)malloc(strlen(mget.mnt_special) + 1);
 150                 strcpy(cachedirp, mget.mnt_special);
 151                 /*
 152                  * AutoClient mounts do not have .cfs_mnt_points string in
 153                  * them, so strstr would return NULL. So .cfs_unmnt file
 154                  * is not updated.
 155                  */
 156                 if ((s = strstr(cachedirp, ".cfs_mnt_points")) != NULL) {
 157                         time32_t btime;
 158 
 159                         xx = -1;
 160                         cachedirp[s-cachedirp] = '\0';
 161                         strcat(cachedirp, CACHEFS_UNMNT_FILE);
 162                         if ((ufd = open(cachedirp, O_WRONLY|O_CREAT|O_TRUNC,
 163                                         0600)) > 0) {
 164                                 if ((btime = get_boottime()) != -1)
 165                                     xx = write(ufd, &btime, sizeof (time32_t));
 166                                 close(ufd);
 167                         }
 168                         if (xx < 0)
 169                                 pr_err(gettext(".cfs_unmnt error"));
 170                 }
 171 
 172                 if (ret != -1) {
 173                         if (mget.mnt_special)
 174                                 strcpy(mnt_back, mget.mnt_special);
 175                 } else {
 176                         mref.mnt_special = mref.mnt_mountp;
 177                         mref.mnt_mountp = NULL;
 178                         rewind(finp);
 179                         ret = getmntany(finp, &mget, &mref);
 180                         if (ret != -1) {
 181                                 strcpy(mnt_front, mget.mnt_mountp);
 182                                 if (mget.mnt_special)
 183                                         strcpy(mnt_back, mget.mnt_special);
 184                         } else {
 185                                 pr_err(gettext("warning: %s not in mnttab"),
 186                                     mref.mnt_special);
 187                         }
 188                 }
 189                 fclose(finp);
 190         }
 191 
 192         /* try to get the daemon to unmount this file system for us */
 193         xx = daemon_unmount(mnt_front, flag);
 194         if (xx == ENOTSUP)
 195                 return (1);
 196         if (xx == EBUSY) {
 197                 pr_err(gettext("%s %s"), mnt_front, strerror(xx));
 198                 return (1);
 199         }
 200         if (xx == EIO) {
 201                 /* try to unmount the file system directly */
 202                 if (umount2(mnt_front, flag) == -1) {
 203                         pr_err(gettext("%s %s"), mnt_front, strerror(errno));
 204                         return (1);
 205                 }
 206         }
 207 
 208         /* if we do not know the name of the back file system mount point */
 209         if (mnt_back[0] == '\0') {
 210                 /* all done */
 211                 return (0);
 212         }
 213 
 214         /*
 215          * If the back file system was mounted on a directory with a
 216          * parent name of BACKMNT_NAME then we assume that we
 217          * mounted it and that it is okay to try to umount it.
 218          */
 219         if (strstr(mnt_back, BACKMNT_NAME) == NULL)
 220                 return (0);
 221 
 222         /* if no back file system mounted */
 223         if (strcmp(mnt_back, "nobackfs") == 0)
 224                 return (0);
 225 
 226         /* fork */
 227         if ((pid = fork()) == -1) {
 228                 pr_err(gettext("could not fork %s"), strerror(errno));
 229                 return (1);
 230         }
 231 
 232         /* if the child */
 233         if (pid == 0) {
 234                 /* invoke the umount command on the back file system */
 235                 xx = execl("/sbin/umount", "/sbin/umount", mnt_back, NULL);
 236                 pr_err(gettext("could not exec /sbin/umount"
 237                     " on back file system %s"), strerror(errno));
 238         }
 239 
 240         /* else if the parent */
 241         else {
 242                 /* wait for the child to exit */
 243                 if (wait(&stat_loc) == -1) {
 244                         pr_err(gettext("wait failed %s"), strerror(errno));
 245                         return (1);
 246                 }
 247 
 248                 if (!WIFEXITED(stat_loc)) {
 249                         pr_err(gettext("back umount did not exit"));
 250                         return (1);
 251                 }
 252 
 253                 xx = WEXITSTATUS(stat_loc);
 254                 if (xx) {
 255                         pr_err(gettext("back umount failed"));
 256                         return (xx);
 257                 }
 258         }
 259 
 260         /* delete the back file system mount point since we created it */
 261         rmdir(mnt_back);
 262 
 263         return (xx);
 264 }
 265 
 266 /*
 267  *
 268  *                      pr_err
 269  *
 270  * Description:
 271  *      Prints an error message to stderr.
 272  * Arguments:
 273  *      fmt     printf style format
 274  *      ...     arguments for fmt
 275  * Returns:
 276  * Preconditions:
 277  *      precond(fmt)
 278  */
 279 
 280 void
 281 pr_err(char *fmt, ...)
 282 {
 283         va_list ap;
 284 
 285         va_start(ap, fmt);
 286         (void) fprintf(stderr, gettext("umount -F cachefs: "));
 287         (void) vfprintf(stderr, fmt, ap);
 288         (void) fprintf(stderr, "\n");
 289         va_end(ap);
 290 }
 291 
 292 /*
 293  *
 294  *                      usage
 295  *
 296  * Description:
 297  *      Prints a usage message.
 298  * Arguments:
 299  *      An optional additional message to be displayed.
 300  * Returns:
 301  * Preconditions:
 302  */
 303 
 304 void
 305 usage(char *msgp)
 306 {
 307         if (msgp)
 308                 pr_err(gettext("%s"), msgp);
 309         fprintf(stderr, gettext("Usage: umount -F cachefs dir"));
 310 }
 311 
 312 /*
 313  *
 314  *                      daemon_unmount
 315  *
 316  * Description:
 317  *      Notifies the cachefsd of an unmount request.
 318  * Arguments:
 319  *      Mount point to unmount.
 320  * Returns:
 321  *      Returns 0 if the cachefsd unmounted the file system
 322  *              EIO if should try unmount directly
 323  *              EBUSY if did not unmount because busy
 324  *              EAGAIN if umounted but should not unmount nfs mount
 325  *
 326  * Preconditions:
 327  *      precond(mntptp)
 328  */
 329 
 330 int
 331 daemon_unmount(char *mntptp, int flag)
 332 {
 333         CLIENT *clnt;
 334         enum clnt_stat retval;
 335         int xx;
 336         int result;
 337         char *hostp;
 338         struct utsname info;
 339         static struct timeval TIMEOUT = { 60*60, 0 };
 340         static struct timeval create_timeout = { 5, 0};
 341         struct cachefsd_fs_unmounted uargs;
 342 
 343         /* get the host name */
 344         xx = uname(&info);
 345         if (xx == -1) {
 346                 pr_err(gettext("cannot get host name, errno %d"), errno);
 347                 return (EIO);
 348         }
 349         hostp = info.nodename;
 350         uargs.mntpt = mntptp;
 351         uargs.flag = flag;
 352 
 353         /* creat the connection to the daemon */
 354         clnt = clnt_create_timed(hostp, CACHEFSDPROG, CACHEFSDVERS, "local",
 355             &create_timeout);
 356         if (clnt == NULL) {
 357                 pr_err(gettext("cachefsd is not running"));
 358                 return (EIO);
 359         }
 360         clnt_control(clnt, CLSET_TIMEOUT, (char *)&TIMEOUT);
 361 
 362         retval = cachefsd_fs_unmounted_1(&uargs, &result, clnt);
 363         if (retval != RPC_SUCCESS) {
 364                 clnt_perror(clnt, gettext("cachefsd is not responding"));
 365                 clnt_destroy(clnt);
 366                 return (EIO);
 367         }
 368 
 369         clnt_destroy(clnt);
 370 
 371         return (result);
 372 }