1 /*
2  * scm_spawn.c
3  *
4  * (C)2000-2011 Marc Huber <Marc.Huber@web.de>
5  * All rights reserved.
6  *
7  */
8 
9 #include "misc/sysconf.h"
10 #include "spawnd_headers.h"
11 #include <grp.h>
12 #include <fcntl.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <sys/uio.h>
16 #include <sys/un.h>
17 #include <unistd.h>
18 #include <sysexits.h>
19 
20 static const char rcsid[] __attribute__ ((used)) = "$Id: spawnd_scm_spawn.c,v 1.27 2019/03/31 09:14:23 marc Exp marc $";
21 
spawnd_cleanup_internal(struct spawnd_context * ctx,int fd)22 void spawnd_cleanup_internal(struct spawnd_context *ctx, int fd __attribute__ ((unused)))
23 {
24     DebugIn(DEBUG_PROC);
25 
26     io_close(ctx->io, ctx->fn);
27 
28     while (io_sched_pop(ctx->io, ctx));
29 
30     if (ctx->is_listener)
31 	spawnd_data.listeners_max--;
32     else {
33 	int i;
34 	common_data.users_cur -= ctx->use;
35 	for (i = 0; i < common_data.servers_cur && ctx != spawnd_data.server_arr[i]; i++);
36 	if (i < --common_data.servers_cur)
37 	    spawnd_data.server_arr[i] = spawnd_data.server_arr[common_data.servers_cur];
38 	set_proctitle(ACCEPT);
39     }
40 
41     free(ctx);
42 
43     DebugOut(DEBUG_PROC);
44 }
45 
spawnd_spawn_child(pid_t * pidp)46 int spawnd_spawn_child(pid_t * pidp)
47 {
48     int socks[2];
49     pid_t pid;
50     int flags;
51     int bufsize = spawnd_data.scm_bufsize;
52     int one = 1;
53     char *argv[10];
54     int i = 0;
55     char *deb = alloca(20);
56 
57     memset(&argv, 0, sizeof(argv));
58 
59     argv[i++] = spawnd_data.child_path;
60     if (common_data.version_only)
61 	argv[i++] = "-v";
62     if (common_data.parse_only)
63 	argv[i++] = "-P";
64     if (common_data.debug) {
65 	argv[i++] = "-d";
66 	snprintf(deb, 20, "%u", common_data.debug);
67 	argv[i++] = deb;
68     }
69     argv[i++] = spawnd_data.child_config;
70     argv[i++] = spawnd_data.child_id;
71     argv[i++] = NULL;
72 
73     if (socketpair(PF_UNIX, SOCK_DGRAM, 0, socks)) {
74 	logerr("socketpair (%s:%d)", __FILE__, __LINE__);
75 	exit(EX_OSERR);
76     }
77 
78     switch ((pid = fork())) {
79     case 0:
80 	io_destroy(common_data.io, NULL);
81 	close(socks[0]);
82 	dup2(socks[1], 0);
83 	close(socks[1]);
84 	if (bufsize) {
85 	    setsockopt(0, SOL_SOCKET, SO_SNDBUF, (char *) &bufsize, (socklen_t) sizeof(bufsize));
86 	    setsockopt(0, SOL_SOCKET, SO_RCVBUF, (char *) &bufsize, (socklen_t) sizeof(bufsize));
87 	}
88 	setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, (socklen_t) sizeof(one));
89 
90 	if (common_data.parse_only)
91 	    execv(spawnd_data.child_path, argv);
92 	else {
93 	    if (spawnd_data.uid)
94 		setgroups(0, NULL);
95 
96 	    if (spawnd_data.gid && setgid(spawnd_data.gid))
97 		logerr("Can't set group id to %d", (int) spawnd_data.gid);
98 
99 	    if (spawnd_data.uid && setuid(spawnd_data.uid))
100 		logerr("Can't set user id to %d", (int) spawnd_data.uid);
101 
102 	    if (spawnd_data.cwd && chdir(spawnd_data.cwd))
103 		logerr("Can't chdir to %s", spawnd_data.cwd);
104 
105 	    execv(spawnd_data.child_path, argv);
106 	}
107 
108 	logerr("execl (%s, ...) (%s:%d)", spawnd_data.child_path, __FILE__, __LINE__);
109 	if (!strchr(spawnd_data.child_path, '/'))
110 	    logmsg("Try calling %s with its absolute path, and this " "problem will go away.", spawnd_data.child_path);
111 	exit(EX_OSERR);
112     case -1:
113 	logerr("fork (%s:%d)", __FILE__, __LINE__);
114 	exit(EX_OSERR);
115     default:
116 	close(socks[1]);
117 	flags = fcntl(socks[0], F_GETFD, 0) | FD_CLOEXEC;
118 	fcntl(socks[0], F_SETFD, flags);
119 	if (bufsize) {
120 	    setsockopt(socks[0], SOL_SOCKET, SO_SNDBUF, (char *) &bufsize, (socklen_t) sizeof(bufsize));
121 	    setsockopt(socks[0], SOL_SOCKET, SO_RCVBUF, (char *) &bufsize, (socklen_t) sizeof(bufsize));
122 	}
123 	setsockopt(socks[0], SOL_SOCKET, SO_KEEPALIVE, (char *) &one, (socklen_t) sizeof(one));
124 	if (pidp)
125 	    *pidp = pid;
126 
127 	return socks[0];
128     }
129 }
130 
recv_childmsg(struct spawnd_context * ctx,int cur)131 static void recv_childmsg(struct spawnd_context *ctx, int cur)
132 {
133     int max = -1;
134     struct scm_data_accept sd;
135     int result = common_data.scm_recv_msg(cur, &sd, sizeof(sd), NULL);
136 
137     if (result)
138 	spawnd_cleanup_internal(ctx, cur);
139     else
140 	switch (sd.type) {
141 	case SCM_DONE:
142 	    common_data.users_cur--, ctx->use--;
143 	    if (spawnd_data.listeners_inactive) {
144 		int i;
145 		logmsg("resuming normal operation");
146 		spawnd_data.listeners_inactive = 0;
147 		switch (spawnd_data.overload) {
148 		case S_queue:
149 		    for (i = 0; i < spawnd_data.listeners_max; i++) {
150 			if (spawnd_data.listener_arr[i]->listen_backlog != spawnd_data.listener_arr[i]->overload_backlog)
151 			    listen(spawnd_data.listener_arr[i]->fn, spawnd_data.listener_arr[i]->listen_backlog);
152 			io_set_i(ctx->io, spawnd_data.listener_arr[i]->fn);
153 		    }
154 		    break;
155 		case S_reset:
156 		    for (i = 0; i < spawnd_data.listeners_max; i++)
157 			spawnd_bind_listener(spawnd_data.listener_arr[i], spawnd_data.listener_arr[i]->fn);
158 		    break;
159 		default:;
160 		}
161 	    }
162 	    set_proctitle(ACCEPT);
163 	    break;
164 	case SCM_BAD_CFG:
165 	    logmsg("Child reported fatal configuration problem. Exiting.");
166 	    exit(EX_CONFIG);
167 	case SCM_DYING:
168 	    spawnd_cleanup_internal(ctx, cur);
169 	    break;
170 	case SCM_MAX:
171 	    max = ((struct scm_data_max *) (&sd))->max;
172 	    if (common_data.users_max > max) {
173 		common_data.users_max = max;
174 		logmsg("child limits maximum number of users to %d", common_data.users_max);
175 		set_proctitle(ACCEPT);
176 	    }
177 	    break;
178 	case SCM_KEEPALIVE:
179 	    break;
180 	default:
181 	    logmsg("Child used unknown message type %d", (int) sd.type);
182 	}
183 }
184 
spawnd_add_child()185 void spawnd_add_child()
186 {
187     if (common_data.servers_cur < common_data.servers_max) {
188 	pid_t pid;
189 	int cur = spawnd_spawn_child(&pid);
190 	if (cur > -1) {
191 	    struct spawnd_context *ctx = spawnd_new_context(common_data.io);
192 	    ctx->pid = pid;
193 	    ctx->fn = cur;
194 	    ctx->tv = io_now;
195 
196 	    io_register(common_data.io, cur, ctx);
197 	    io_set_cb_i(common_data.io, cur, (void *) recv_childmsg);
198 	    io_set_cb_h(common_data.io, cur, (void *) spawnd_cleanup_internal);
199 	    io_set_cb_e(common_data.io, cur, (void *) spawnd_cleanup_internal);
200 	    io_clr_cb_o(common_data.io, cur);
201 	    io_set_i(common_data.io, cur);
202 	    spawnd_data.server_arr[common_data.servers_cur++] = ctx;
203 	}
204     }
205 }
206