diff --git a/usr.sbin/nfsd/Makefile b/usr.sbin/nfsd/Makefile --- a/usr.sbin/nfsd/Makefile +++ b/usr.sbin/nfsd/Makefile @@ -3,4 +3,6 @@ PROG= nfsd MAN= nfsd.8 nfsv4.4 stablerestart.5 pnfs.4 pnfsserver.4 +LIBADD= util + .include diff --git a/usr.sbin/nfsd/Makefile.depend b/usr.sbin/nfsd/Makefile.depend --- a/usr.sbin/nfsd/Makefile.depend +++ b/usr.sbin/nfsd/Makefile.depend @@ -9,6 +9,7 @@ lib/${CSU_DIR} \ lib/libc \ lib/libcompiler_rt \ + lib/libutil \ .include diff --git a/usr.sbin/nfsd/nfsd.8 b/usr.sbin/nfsd/nfsd.8 --- a/usr.sbin/nfsd/nfsd.8 +++ b/usr.sbin/nfsd/nfsd.8 @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd February 21, 2025 +.Dd May 30, 2025 .Dt NFSD 8 .Os .Sh NAME @@ -39,6 +39,7 @@ .Op Fl h Ar bindip .Op Fl p Ar pnfs_setup .Op Fl m Ar mirror_level +.Op Fl P Ar pidfile .Op Fl V Ar virtual_hostname .Op Fl Fl maxthreads Ar max_threads .Op Fl Fl minthreads Ar min_threads @@ -84,6 +85,10 @@ Unregister the NFS service with .Xr rpcbind 8 without creating any servers. +.It Fl P Ar pidfile +Specify alternative location of a file where main process PID will be stored. +The default location is +.Pa /var/run/nfsd.pid . .It Fl V Ar virtual_hostname Specifies a hostname to be used as a principal name, instead of the default hostname. diff --git a/usr.sbin/nfsd/nfsd.c b/usr.sbin/nfsd/nfsd.c --- a/usr.sbin/nfsd/nfsd.c +++ b/usr.sbin/nfsd/nfsd.c @@ -58,6 +58,7 @@ #include #include +#include #include #include #include @@ -70,6 +71,7 @@ static int debug = 0; static int nofork = 0; +#define DEFAULT_PIDFILE "/var/run/nfsd.pid" #define NFSD_STABLERESTART "/var/db/nfs-stablerestart" #define NFSD_STABLEBACKUP "/var/db/nfs-stablerestart.bak" #define MAXNFSDCNT 256 @@ -79,6 +81,7 @@ #define NFS_VER4 4 static pid_t children[MAXNFSDCNT]; /* PIDs of children */ static pid_t masterpid; /* PID of master/parent */ +static struct pidfh *masterpidfh = NULL; /* pidfh of master/parent */ static int nfsdcnt; /* number of children */ static int nfsdcnt_set; static int minthreads; @@ -161,7 +164,8 @@ size_t jailed_size, nfs_minvers_size; const char *lopt; char **bindhost = NULL; - pid_t pid; + const char *pidfile_path = DEFAULT_PIDFILE; + pid_t pid, otherpid; struct nfsd_nfsd_args nfsdargs; const char *vhostname = NULL; @@ -171,14 +175,14 @@ nfsdcnt = DEFNFSDCNT; unregister = reregister = tcpflag = maxsock = 0; bindanyflag = udpflag = connect_type_cnt = bindhostc = 0; - getopt_shortopts = "ah:n:rdtuep:m:V:N"; + getopt_shortopts = "ah:n:rdtuep:m:V:NP:"; getopt_usage = "usage:\n" " nfsd [-ardtueN] [-h bindip]\n" " [-n numservers] [--minthreads #] [--maxthreads #]\n" " [-p/--pnfs dsserver0:/dsserver0-mounted-on-dir,...," "dsserverN:/dsserverN-mounted-on-dir] [-m mirrorlevel]\n" - " [-V virtual_hostname]\n"; + " [-P pidfile ] [-V virtual_hostname]\n"; while ((ch = getopt_long(argc, argv, getopt_shortopts, longopts, &longindex)) != -1) switch (ch) { @@ -234,6 +238,9 @@ case 'N': nofork = 1; break; + case 'P': + pidfile_path = optarg; + break; case 0: lopt = longopts[longindex].name; if (!strcmp(lopt, "minthreads")) { @@ -415,6 +422,16 @@ } exit (0); } + + if (pidfile_path != NULL) { + masterpidfh = pidfile_open(pidfile_path, 0600, &otherpid); + if (masterpidfh == NULL) { + if (errno == EEXIST) + errx(1, "daemon already running, pid: %jd.", + (intmax_t)otherpid); + warn("cannot open pid file"); + } + } if (debug == 0 && nofork == 0) { daemon(0, 0); (void)signal(SIGHUP, SIG_IGN); @@ -434,6 +451,9 @@ openlog("nfsd", LOG_PID | (debug ? LOG_PERROR : 0), LOG_DAEMON); + if (masterpidfh != NULL && pidfile_write(masterpidfh) != 0) + syslog(LOG_ERR, "pidfile_write(): %m"); + /* * For V4, we open the stablerestart file and call nfssvc() * to get it loaded. This is done before the daemons do the @@ -490,6 +510,7 @@ if (pid) { children[0] = pid; } else { + pidfile_close(masterpidfh); (void)signal(SIGUSR1, child_cleanup); setproctitle("server"); start_server(0, &nfsdargs, vhostname); @@ -1008,6 +1029,8 @@ { killchildren(); unregistration(); + if (masterpidfh != NULL) + pidfile_remove(masterpidfh); exit(status); } diff --git a/usr.sbin/rpcbind/Makefile b/usr.sbin/rpcbind/Makefile --- a/usr.sbin/rpcbind/Makefile +++ b/usr.sbin/rpcbind/Makefile @@ -9,6 +9,8 @@ CFLAGS+= -DPORTMAP +LIBADD= util + .if ${MK_INET6_SUPPORT} != "no" CFLAGS+= -DINET6 .endif diff --git a/usr.sbin/rpcbind/Makefile.depend b/usr.sbin/rpcbind/Makefile.depend --- a/usr.sbin/rpcbind/Makefile.depend +++ b/usr.sbin/rpcbind/Makefile.depend @@ -9,6 +9,7 @@ lib/${CSU_DIR} \ lib/libc \ lib/libcompiler_rt \ + lib/libutil \ .include diff --git a/usr.sbin/rpcbind/rpcbind.8 b/usr.sbin/rpcbind/rpcbind.8 --- a/usr.sbin/rpcbind/rpcbind.8 +++ b/usr.sbin/rpcbind/rpcbind.8 @@ -1,6 +1,6 @@ .\" Copyright 1989 AT&T .\" Copyright 1991 Sun Microsystems, Inc. -.Dd July 11, 2024 +.Dd May 30, 2025 .Dt RPCBIND 8 .Os .Sh NAME @@ -8,7 +8,7 @@ .Nd universal addresses to RPC program number mapper .Sh SYNOPSIS .Nm -.Op Fl 6adIiLlNswW +.Op Fl 6adIiLlNPswW .Op Fl h Ar bindip .Sh DESCRIPTION The @@ -135,6 +135,10 @@ In this mode, .Nm will not fork when it starts. +.It Fl P +Specify alternative location of a file where main process PID will be stored. +The default location is +.Pa /var/run/rpcbind.pid . .It Fl s Cause .Nm diff --git a/usr.sbin/rpcbind/rpcbind.c b/usr.sbin/rpcbind/rpcbind.c --- a/usr.sbin/rpcbind/rpcbind.c +++ b/usr.sbin/rpcbind/rpcbind.c @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -86,6 +87,11 @@ #define RPCBINDDLOCK "/var/run/rpcbind.lock" +#define DEFAULT_PIDFILE "/var/run/rpcbind.pid" + +char *pidfile_path = DEFAULT_PIDFILE; +struct pidfh *pidfh = NULL; + static int runasdaemon = 0; int insecure = 0; int oldstyle_local = 0; @@ -135,6 +141,7 @@ static int init_transport(struct netconfig *); static void rbllist_add(rpcprog_t, rpcvers_t, struct netconfig *, struct netbuf *); +static void cleanup_pidfile(void); static void terminate(int); static void parseargs(int, char *[]); static void update_bound_sa(void); @@ -163,6 +170,13 @@ if (flock(rpcbindlockfd, LOCK_EX|LOCK_NB) != 0 && errno == EWOULDBLOCK) errx(1, "another rpcbind is already running. Aborting"); + if (pidfile_path != NULL) { + pidfh = pidfile_open(pidfile_path, 0600, NULL); + if (pidfh == NULL) + warn("cannot open pid file"); + atexit(cleanup_pidfile); + } + getrlimit(RLIMIT_NOFILE, &rl); if (rl.rlim_cur < 128) { if (rl.rlim_max <= 128) @@ -248,6 +262,9 @@ err(1, "fork failed"); } + if (pidfh != NULL && pidfile_write(pidfh) != 0) + syslog(LOG_ERR, "pidfile_write(): %m"); + if (runasdaemon) { struct passwd *p; @@ -781,6 +798,16 @@ list_rbl = rbl; } +/* + * atexit callback for pidfh cleanup + */ +static void +cleanup_pidfile(void) +{ + if (pidfh != NULL) + pidfile_remove(pidfh); +} + /* * Catch the signal and die */ @@ -792,8 +819,15 @@ doterminate = signum; wr = write(terminate_wfd, &c, 1); - if (wr < 1) + if (wr < 1) { + /* + * The call to cleanup_pidfile should be async-signal safe. + * pidfile_remove calls fstat and funlinkat system calls, and + * we are exiting immediately. + */ + cleanup_pidfile(); _exit(2); + } } void @@ -821,7 +855,7 @@ #else #define WRAPOP "" #endif - while ((c = getopt(argc, argv, "6adh:IiLlNs" WRAPOP WSOP)) != -1) { + while ((c = getopt(argc, argv, "6adh:IiLlNP:s" WRAPOP WSOP)) != -1) { switch (c) { case '6': ipv6_only = 1; @@ -860,6 +894,9 @@ case 's': runasdaemon = 1; break; + case 'P': + pidfile_path = strdup(optarg); + break; #ifdef LIBWRAP case 'W': libwrap = 1; @@ -872,7 +909,7 @@ #endif default: /* error */ fprintf(stderr, - "usage: rpcbind [-6adIiLls%s%s] [-h bindip]\n", + "usage: rpcbind [-6adIiLlNPs%s%s] [-h bindip]\n", WRAPOP, WSOP); exit (1); }