/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "automount.h" #include #include #include #include /* for private dupcache routines */ #include #include #include #include #include static void autofs_prog(struct svc_req *, SVCXPRT *); static void autofs_mount_1_r(struct autofs_lookupargs *, struct autofs_mountres *, struct authunix_parms *); static void autofs_mount_1_free_r(struct autofs_mountres *); static void autofs_lookup_1_r(struct autofs_lookupargs *, struct autofs_lookupres *, struct authunix_parms *); static void autofs_lookup_1_free_r(struct autofs_lookupres *); static void autofs_unmount_1_r(struct umntrequest *, struct umntres *, struct authunix_parms *); static void autofs_unmount_1_free_r(struct umntres *); static void autofs_readdir_1_r(struct autofs_rddirargs *, struct autofs_rddirres *, struct authunix_parms *); static void autofs_readdir_1_free_r(struct autofs_rddirres *); static void usage(); static void warn_hup(int); static void free_action_list(); static int dupreq_nonidemp(struct svc_req *, SVCXPRT *, int, bool_t (*)(), void (*)()); static int dupdonereq_nonidemp(struct svc_req *, caddr_t, bool_t (*)()); static int dupreq_idemp(struct svc_req *, SVCXPRT *, int, bool_t (*)(), void (*)()); static int dupdonereq_idemp(struct svc_req *, caddr_t, bool_t (*)()); #define CTIME_BUF_LEN 26 /* * XXX - this limit was imposed due to resource problems - even though * we can and do try and set the rlimit to be able to handle more threads, * fopen() doesn't allow more than 256 fp's. */ #define MAXTHREADS 64 #define RESOURCE_FACTOR 8 static char str_arch[32]; static char str_cpu[32]; struct autodir *dir_head; struct autodir *dir_tail; char self[64]; time_t timenow; int verbose = 0; int trace = 0; int automountd_nobrowse = 0; int main(argc, argv) int argc; char *argv[]; { pid_t pid; int c, i, error; struct rlimit rlset; int rpc_svc_mode = RPC_SVC_MT_AUTO; int maxthreads = MAXTHREADS; int prevthreads = 0; char *defval; int defflags; if (geteuid() != 0) { (void) fprintf(stderr, "%s must be run as root\n", argv[0]); exit(1); } /* * Read in the values from config file first before we check * commandline options so the options override the file. */ if ((defopen(AUTOFSADMIN)) == 0) { if ((defval = defread("AUTOMOUNTD_VERBOSE=")) != NULL) { if (strncasecmp("true", defval, 4) == 0) verbose = TRUE; else verbose = FALSE; } if ((defval = defread("AUTOMOUNTD_NOBROWSE=")) != NULL) { if (strncasecmp("true", defval, 4) == 0) automountd_nobrowse = TRUE; else automountd_nobrowse = FALSE; } if ((defval = defread("AUTOMOUNTD_TRACE=")) != NULL) { errno = 0; trace = strtol(defval, (char **)NULL, 10); if (errno != 0) trace = 0; } if ((defval = defread("AUTOMOUNTD_ENV=")) != NULL) { (void) putenv(strdup(defval)); defflags = defcntl(DC_GETFLAGS, 0); TURNON(defflags, DC_NOREWIND); defflags = defcntl(DC_SETFLAGS, defflags); while ((defval = defread("AUTOMOUNTD_ENV=")) != NULL) (void) putenv(strdup(defval)); (void) defcntl(DC_SETFLAGS, defflags); } /* close defaults file */ defopen(NULL); } while ((c = getopt(argc, argv, "vnTD:")) != EOF) { switch (c) { case 'v': verbose++; break; case 'n': automountd_nobrowse++; break; case 'T': trace++; break; case 'D': (void) putenv(optarg); break; default: usage(); } } if (sysinfo(SI_HOSTNAME, self, sizeof (self)) == -1) { error = errno; fprintf(stderr, "automountd: can't determine hostname, error: %d\n", error); exit(1); } #ifndef DEBUG pid = fork(); if (pid < 0) { perror("cannot fork"); exit(1); } if (pid) exit(0); #endif (void) setsid(); openlog("automountd", LOG_PID, LOG_DAEMON); (void) setlocale(LC_ALL, ""); /* * Since the "arch" command no longer exists we * have to rely on sysinfo(SI_MACHINE) to return the closest * approximation. For backward compatibility we * need to substitute "sun4" for "sun4m", "sun4c", ... */ if (getenv("ARCH") == NULL) { char buf[16]; if (sysinfo(SI_MACHINE, buf, sizeof (buf)) != -1) { if (strncmp(buf, "sun4", 4) == 0) (void) strcpy(buf, "sun4"); (void) sprintf(str_arch, "ARCH=%s", buf); (void) putenv(str_arch); } else { syslog(LOG_ERR, "can't determine machine type, error: %m"); } } if (getenv("CPU") == NULL) { char buf[16]; if (sysinfo(SI_ARCHITECTURE, buf, sizeof (buf)) != -1) { (void) sprintf(str_cpu, "CPU=%s", buf); (void) putenv(str_cpu); } else { syslog(LOG_ERR, "can't determine processor type, error: %m"); } } (void) rwlock_init(&cache_lock, USYNC_THREAD, NULL); (void) rwlock_init(&rddir_cache_lock, USYNC_THREAD, NULL); /* * initialize the name services, use NULL arguments to ensure * we don't initialize the stack of files used in file service */ (void) ns_setup(NULL, NULL); /* * set the maximum number of threads to be used. If it succeeds * increase the number of resources the threads need. If the * the resource allocation fails, return the threads value back * to the default value */ if (((rpc_control(RPC_SVC_THRMAX_GET, &prevthreads)) == TRUE) && ((rpc_control(RPC_SVC_THRMAX_SET, &maxthreads)) == TRUE)) { rlset.rlim_max = RESOURCE_FACTOR * maxthreads; rlset.rlim_cur = RESOURCE_FACTOR * maxthreads; if ((setrlimit(RLIMIT_NOFILE, &rlset)) != 0) { syslog(LOG_ERR, "unable to increase system resource limit"); /* back off changes to threads */ if ((rpc_control(RPC_SVC_THRMAX_SET, &prevthreads)) == FALSE) { /* * Exit if we have more threads than resources. */ syslog(LOG_ERR, "unable to match threads to system resources"); exit(1); } syslog(LOG_ERR, "decreased threads to match low resources"); } else { /* * Both are successful. Note that setrlimit * allows a max setting of 1024 */ if (trace > 3) { trace_prt(1, " maxthreads: %d rlim_max: %d rlim_cur: %d\n", maxthreads, rlset.rlim_max, rlset.rlim_cur); } closefrom(3); } (void) enable_extended_FILE_stdio(-1, -1); } else { syslog(LOG_ERR, "unable to increase threads - continue with default"); } /* * establish our lock on the lock file and write our pid to it. * exit if some other process holds the lock, or if there's any * error in writing/locking the file. */ pid = _enter_daemon_lock(AUTOMOUNTD); switch (pid) { case 0: break; case -1: syslog(LOG_ERR, "error locking for %s: %s", AUTOMOUNTD, strerror(errno)); exit(2); default: /* daemon was already running */ exit(0); } /* * If we coredump it'll be /core. */ if (chdir("/") < 0) syslog(LOG_ERR, "chdir /: %m"); /* * Create cache_cleanup thread */ if (thr_create(NULL, 0, (void *(*)(void *))cache_cleanup, NULL, THR_DETACHED | THR_DAEMON | THR_NEW_LWP, NULL)) { syslog(LOG_ERR, "unable to create cache_cleanup thread"); exit(1); } /* other initializations */ (void) rwlock_init(&portmap_cache_lock, USYNC_THREAD, NULL); /* on a labeled system, the automounter implements read-down policy */ if (is_system_labeled()) { if ((setpflags(NET_MAC_AWARE, 1) == -1) || (setpflags(NET_MAC_AWARE_INHERIT, 1) == -1)) syslog(LOG_ERR, "ignored failure to set MAC-aware " "mode: %m"); } if (!rpc_control(RPC_SVC_MTMODE_SET, &rpc_svc_mode)) { syslog(LOG_ERR, "unable to set automatic MT mode"); exit(1); } if (svc_create_local_service(autofs_prog, AUTOFS_PROG, AUTOFS_VERS, "netpath", "autofs") == 0) { syslog(LOG_ERR, "unable to create service"); exit(1); } (void) signal(SIGHUP, warn_hup); svc_run(); syslog(LOG_ERR, "svc_run returned"); return (1); } /* * The old automounter supported a SIGHUP * to allow it to resynchronize internal * state with the /etc/mnttab. * This is no longer relevant, but we * need to catch the signal and warn * the user. */ /* ARGSUSED */ static void warn_hup(i) int i; { syslog(LOG_ERR, "SIGHUP received: ignored"); (void) signal(SIGHUP, warn_hup); } static void usage() { (void) fprintf(stderr, "Usage: automountd\n" "\t[-T]\t\t(trace requests)\n" "\t[-v]\t\t(verbose error msgs)\n" "\t[-D n=s]\t(define env variable)\n"); exit(1); /* NOTREACHED */ } /* * dupreq_nonidemp(struct svc_req *rqstp, SVCXPRT *transp, int res_sz, * bool_t (*xdr_result)(), void (*local_free)()) * check the status of nonidempotent requests in the duplicate request cache. * Get result of done requests and send a reply to the kernel. Return status. */ static int dupreq_nonidemp(struct svc_req *rqstp, SVCXPRT *transp, int res_sz, bool_t (*xdr_result)(), void (*local_free)()) { caddr_t resp_buf; uint_t resp_bufsz; int dupstat; XDR xdrs; caddr_t res; dupstat = __svc_vc_dup(rqstp, &resp_buf, &resp_bufsz); switch (dupstat) { case DUP_NEW: break; case DUP_DONE: if (!resp_buf) { if (verbose) { syslog(LOG_ERR, "dupreq_nonidemp: done, no cached result"); } break; } /* buffer contains xdr encoded results - decode and sendreply */ if (verbose) { syslog(LOG_ERR, "dupreq_nonidemp: done, send reply to kernel"); } memset((caddr_t)&xdrs, 0, sizeof (XDR)); xdrmem_create(&xdrs, resp_buf, resp_bufsz, XDR_DECODE); if ((res = (caddr_t)malloc(res_sz)) == NULL) { syslog(LOG_ERR, "dupreq_nonidemp: out of memory"); xdr_destroy(&xdrs); free(resp_buf); break; } memset(res, 0, res_sz); if ((*xdr_result)(&xdrs, res) == FALSE) { if (verbose) syslog(LOG_ERR, "dupreq_nonidemp: cannot xdr decode result"); xdr_destroy(&xdrs); free(resp_buf); free(res); break; } if (!svc_sendreply(transp, xdr_result, (caddr_t)res)) { xdr_destroy(&xdrs); free(resp_buf); (void) (*local_free)(res); free(res); svcerr_systemerr(transp); return (DUP_ERROR); } xdr_destroy(&xdrs); free(resp_buf); (void) (*local_free)(res); free(res); break; /* all other cases log the case and drop the request */ case DUP_INPROGRESS: if (verbose) { syslog(LOG_ERR, "dupreq_nonidemp: duplicate request in progress\n"); } break; case DUP_DROP: /* should never be called in automountd */ if (verbose) syslog(LOG_ERR, "dupreq_nonidemp: dropped duplicate request error"); break; case DUP_ERROR: /* fall through */ default: if (verbose) syslog(LOG_ERR, "dupreq_nonidemp: duplicate request cache error"); break; } return (dupstat); } /* * dupdonereq_nonidemp(struct svc_req *rqstp, caddr_t res, * bool_t (*xdr_result)()) * call the cache to indicate we are done with the nonidempotent request. * xdr_result will write the encoded xdr form of results into the buffer * provided in xdrmem_create. Makes a best effort to update the cache * first with a buffer containing the results, and then with a NULL buffer. * Return status. */ static int dupdonereq_nonidemp(struct svc_req *rqstp, caddr_t res, bool_t (*xdr_result)()) { caddr_t resp_buf; ulong_t resp_bufsz; XDR xdrs; int dupstat; /* * create a results buffer and write into the cache * continue with a NULL buffer on errors. */ if ((resp_bufsz = xdr_sizeof(xdr_result, (void *)res)) == 0) { if (verbose) syslog(LOG_ERR, "dupdonereq_nonidemp: xdr error"); resp_buf = NULL; resp_bufsz = 0; } else { if ((resp_buf = (caddr_t)malloc(resp_bufsz)) == NULL) { syslog(LOG_ERR, "dupdonereq_nonidemp: out of memory"); resp_bufsz = 0; } else { memset(resp_buf, 0, resp_bufsz); memset((caddr_t)&xdrs, 0, sizeof (XDR)); xdrmem_create(&xdrs, resp_buf, (uint_t)resp_bufsz, XDR_ENCODE); if ((*xdr_result)(&xdrs, res) == FALSE) { if (verbose) syslog(LOG_ERR, "cannot xdr encode results"); xdr_destroy(&xdrs); free(resp_buf); resp_buf = NULL; resp_bufsz = 0; } else xdr_destroy(&xdrs); } } dupstat = __svc_vc_dupdone(rqstp, resp_buf, (uint_t)resp_bufsz, DUP_DONE); if (dupstat == DUP_ERROR) { if (verbose) syslog(LOG_ERR, "dupdonereq_nonidemp: cache error"); if (resp_buf != NULL) { if (verbose) syslog(LOG_ERR, "dupdonereq_nonidemp: retry"); dupstat = __svc_vc_dupdone(rqstp, NULL, 0, DUP_DONE); if ((dupstat == DUP_ERROR) && verbose) syslog(LOG_ERR, "dupdonereq_nonidemp: retry failed"); } } if (resp_buf) free(resp_buf); return (dupstat); } /* * dupreq_idemp(struct svc_req *rqstp, SVCXPRT *transp, int res_sz; * bool_t (*xdr_result)(), void (*local_free)()) * check the status of idempotent requests in the duplicate request cache. * treat a idempotent request like a new one if its done, but do workavoids * if its a request in progress. Return status. */ static int dupreq_idemp(struct svc_req *rqstp, SVCXPRT *transp, int res_sz, bool_t (*xdr_result)(), void (*local_free)()) { int dupstat; #ifdef lint transp = transp; res_sz = res_sz; local_free = local_free; xdr_result = xdr_result; #endif /* lint */ /* * call the cache to check the status of the request. don't care * about results in the cache. */ dupstat = __svc_vc_dup(rqstp, NULL, NULL); switch (dupstat) { case DUP_NEW: break; case DUP_DONE: if (verbose) syslog(LOG_ERR, "dupreq_idemp: done request, redo"); dupstat = DUP_NEW; break; /* all other cases log the case and drop the request */ case DUP_INPROGRESS: if (verbose) syslog(LOG_ERR, "dupreq_idemp: duplicate request in progress\n"); break; case DUP_DROP: /* should never be called in automountd */ if (verbose) syslog(LOG_ERR, "dupreq_idemp: dropped duplicate request error"); break; case DUP_ERROR: /* fall through */ default: if (verbose) syslog(LOG_ERR, "dupreq_idemp: duplicate request cache error"); break; } return (dupstat); } /* * dupdonereq_idemp(struct svc_req *rqstp, caddr_t res, bool_t (*xdr_result)()) * call the cache to indicate we are done with the idempotent request - we do * this to allow work avoids for in progress requests. don't bother to store * any results in the cache. Return status. */ static int dupdonereq_idemp(struct svc_req *rqstp, caddr_t res, bool_t (*xdr_result)()) { int dupstat; #ifdef lint res = res; xdr_result = xdr_result; #endif /* lint */ dupstat = __svc_vc_dupdone(rqstp, NULL, (uint_t)0, DUP_DONE); if ((dupstat == DUP_ERROR) && verbose) syslog(LOG_ERR, "dupdonereq_idemp: cannot cache result"); return (dupstat); } /* * Returns the UID of the caller */ static uid_t getowner(transp) SVCXPRT *transp; { uid_t uid; if (__rpc_get_local_uid(transp, &uid) < 0) { char *err_msg = "Could not get local uid - request ignored\n"; if (trace > 1) trace_prt(1, err_msg); if (verbose) pr_msg(err_msg); return (-1); } if (uid != 0) { char *err_msg = "Illegal access attempt by uid=%ld - request ignored\n"; if (trace > 1) trace_prt(1, err_msg, uid); pr_msg(err_msg, uid); } return (uid); } /* * Each RPC request will automatically spawn a new thread with this * as its entry point. * XXX - the switch statement should be changed to a table of procedures * similar to that used by rfs_dispatch() in uts/common/fs/nfs/nfs_server.c. * duplicate request handling should also be synced with rfs_dispatch(). */ static void autofs_prog(rqstp, transp) struct svc_req *rqstp; register SVCXPRT *transp; { union { autofs_lookupargs autofs_mount_1_arg; autofs_lookupargs autofs_lookup_1_arg; umntrequest autofs_umount_1_arg; autofs_rddirargs autofs_readdir_1_arg; } argument; union { autofs_mountres mount_res; autofs_lookupres lookup_res; umntres umount_res; autofs_rddirres readdir_res; } res; bool_t (*xdr_argument)(); bool_t (*xdr_result)(); void (*local)(); void (*local_free)(); int (*dup_request)(); int (*dupdone_request)(); timenow = time((time_t *)NULL); if (rqstp->rq_proc != NULLPROC && getowner(transp) != 0) { /* * Drop request */ return; } switch (rqstp->rq_proc) { case NULLPROC: (void) svc_sendreply(transp, xdr_void, (char *)NULL); return; #ifdef MALLOC_DEBUG case AUTOFS_DUMP_DEBUG: (void) svc_sendreply(transp, xdr_void, (char *)NULL); check_leaks("/var/tmp/automountd.leak"); return; #endif case AUTOFS_LOOKUP: xdr_argument = xdr_autofs_lookupargs; xdr_result = xdr_autofs_lookupres; local = autofs_lookup_1_r; local_free = autofs_lookup_1_free_r; dup_request = dupreq_nonidemp; dupdone_request = dupdonereq_nonidemp; break; case AUTOFS_MOUNT: xdr_argument = xdr_autofs_lookupargs; xdr_result = xdr_autofs_mountres; local = autofs_mount_1_r; local_free = autofs_mount_1_free_r; dup_request = dupreq_nonidemp; dupdone_request = dupdonereq_nonidemp; break; case AUTOFS_UNMOUNT: xdr_argument = xdr_umntrequest; xdr_result = xdr_umntres; local = autofs_unmount_1_r; local_free = autofs_unmount_1_free_r; dup_request = dupreq_nonidemp; dupdone_request = dupdonereq_nonidemp; break; case AUTOFS_READDIR: xdr_argument = xdr_autofs_rddirargs; xdr_result = xdr_autofs_rddirres; local = autofs_readdir_1_r; local_free = autofs_readdir_1_free_r; dup_request = dupreq_idemp; dupdone_request = dupdonereq_idemp; break; default: svcerr_noproc(transp); return; } if ((*dup_request)(rqstp, transp, sizeof (res), xdr_result, local_free) != DUP_NEW) return; (void) memset((char *)&argument, 0, sizeof (argument)); if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) { svcerr_decode(transp); return; } (void) memset((char *)&res, 0, sizeof (res)); (*local)(&argument, &res, rqstp->rq_clntcred); /* update cache with done request results */ (void) (*dupdone_request)(rqstp, (caddr_t)&res, xdr_result); if (!svc_sendreply(transp, xdr_result, (caddr_t)&res)) { svcerr_systemerr(transp); } if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) { syslog(LOG_ERR, "unable to free arguments"); } (*local_free)(&res); } static void autofs_readdir_1_r(req, res, cred) struct autofs_rddirargs *req; struct autofs_rddirres *res; struct authunix_parms *cred; { if (trace > 0) trace_prt(1, "READDIR REQUEST : %s @ %ld\n", req->rda_map, req->rda_offset); (void) do_readdir(req, res, cred); if (trace > 0) trace_prt(1, "READDIR REPLY : status=%d\n", res->rd_status); } static void autofs_readdir_1_free_r(res) struct autofs_rddirres *res; { if (res->rd_status == AUTOFS_OK) { if (res->rd_rddir.rddir_entries) free(res->rd_rddir.rddir_entries); } } /* ARGSUSED */ static void autofs_unmount_1_r(m, res, cred) struct umntrequest *m; struct umntres *res; struct authunix_parms *cred; { struct umntrequest *ul; if (trace > 0) { char ctime_buf[CTIME_BUF_LEN]; if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL) ctime_buf[0] = '\0'; trace_prt(1, "UNMOUNT REQUEST: %s", ctime_buf); for (ul = m; ul; ul = ul->next) trace_prt(1, " resource=%s fstype=%s mntpnt=%s" " mntopts=%s %s\n", ul->mntresource, ul->fstype, ul->mntpnt, ul->mntopts, ul->isdirect ? "direct" : "indirect"); } res->status = do_unmount1(m); if (trace > 0) trace_prt(1, "UNMOUNT REPLY: status=%d\n", res->status); } static void autofs_unmount_1_free_r(res) struct umntres *res; { #ifdef lint res = res; #endif /* lint */ } static void autofs_lookup_1_r(m, res, cred) struct autofs_lookupargs *m; struct autofs_lookupres *res; struct authunix_parms *cred; { enum autofs_action action; struct linka link; int status; if (trace > 0) { char ctime_buf[CTIME_BUF_LEN]; if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL) ctime_buf[0] = '\0'; trace_prt(1, "LOOKUP REQUEST: %s", ctime_buf); trace_prt(1, " name=%s[%s] map=%s opts=%s path=%s direct=%d\n", m->name, m->subdir, m->map, m->opts, m->path, m->isdirect); } status = do_lookup1(m->map, m->name, m->subdir, m->opts, m->path, (uint_t)m->isdirect, &action, &link, cred); if (status == 0) { /* * Return action list to kernel. */ res->lu_res = AUTOFS_OK; if ((res->lu_type.action = action) == AUTOFS_LINK_RQ) res->lu_type.lookup_result_type_u.lt_linka = link; } else { /* * Entry not found */ res->lu_res = AUTOFS_NOENT; } res->lu_verbose = verbose; if (trace > 0) trace_prt(1, "LOOKUP REPLY : status=%d\n", res->lu_res); } static void autofs_lookup_1_free_r(res) struct autofs_lookupres *res; { struct linka link; if ((res->lu_res == AUTOFS_OK) && (res->lu_type.action == AUTOFS_LINK_RQ)) { /* * Free link information */ link = res->lu_type.lookup_result_type_u.lt_linka; if (link.dir) free(link.dir); if (link.link) free(link.link); } } static void autofs_mount_1_r(m, res, cred) struct autofs_lookupargs *m; struct autofs_mountres *res; struct authunix_parms *cred; { int status; action_list *alp = NULL; if (trace > 0) { char ctime_buf[CTIME_BUF_LEN]; if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL) ctime_buf[0] = '\0'; trace_prt(1, "MOUNT REQUEST: %s", ctime_buf); trace_prt(1, " name=%s[%s] map=%s opts=%s path=%s direct=%d\n", m->name, m->subdir, m->map, m->opts, m->path, m->isdirect); } status = do_mount1(m->map, m->name, m->subdir, m->opts, m->path, (uint_t)m->isdirect, &alp, cred); if (status != 0) { /* * An error occurred, free action list if allocated. */ if (alp != NULL) { free_action_list(alp); alp = NULL; } } if (alp != NULL) { /* * Return action list to kernel. */ res->mr_type.status = AUTOFS_ACTION; res->mr_type.mount_result_type_u.list = alp; } else { /* * No work to do left for the kernel */ res->mr_type.status = AUTOFS_DONE; res->mr_type.mount_result_type_u.error = status; } res->mr_verbose = verbose; if (trace > 0) { switch (res->mr_type.status) { case AUTOFS_ACTION: trace_prt(1, "MOUNT REPLY : status=%d, AUTOFS_ACTION\n", status); break; case AUTOFS_DONE: trace_prt(1, "MOUNT REPLY : status=%d, AUTOFS_DONE\n", status); break; default: trace_prt(1, "MOUNT REPLY : status=%d, UNKNOWN\n", status); } } if (status && verbose) { if (m->isdirect) { /* direct mount */ syslog(LOG_ERR, "mount of %s failed", m->path); } else { /* indirect mount */ syslog(LOG_ERR, "mount of %s/%s failed", m->path, m->name); } } } static void autofs_mount_1_free_r(res) struct autofs_mountres *res; { if (res->mr_type.status == AUTOFS_ACTION) { if (trace > 2) trace_prt(1, "freeing action list\n"); free_action_list(res->mr_type.mount_result_type_u.list); } } /* * Used for reporting messages from code shared with automount command. * Formats message into a buffer and calls syslog. * * Print an error. Works like printf (fmt string and variable args) * except that it will subsititute an error message for a "%m" string * (like syslog). */ void pr_msg(const char *fmt, ...) { va_list ap; char fmtbuff[BUFSIZ], buff[BUFSIZ]; const char *p1; char *p2; p2 = fmtbuff; fmt = gettext(fmt); for (p1 = fmt; *p1; p1++) { if (*p1 == '%' && *(p1 + 1) == 'm') { (void) strcpy(p2, strerror(errno)); p2 += strlen(p2); p1++; } else { *p2++ = *p1; } } if (p2 > fmtbuff && *(p2-1) != '\n') *p2++ = '\n'; *p2 = '\0'; va_start(ap, fmt); (void) vsprintf(buff, fmtbuff, ap); va_end(ap); syslog(LOG_ERR, buff); } static void free_action_list(action_list *alp) { action_list *p, *next = NULL; struct mounta *mp; for (p = alp; p != NULL; p = next) { switch (p->action.action) { case AUTOFS_MOUNT_RQ: mp = &(p->action.action_list_entry_u.mounta); /* LINTED pointer alignment */ free_autofs_args((autofs_args *)mp->dataptr); mp->dataptr = NULL; mp->datalen = 0; free_mounta(mp); break; case AUTOFS_LINK_RQ: syslog(LOG_ERR, "non AUTOFS_MOUNT_RQ requests not implemented\n"); break; default: syslog(LOG_ERR, "non AUTOFS_MOUNT_RQ requests not implemented\n"); break; } next = p->next; free(p); } }