xref: /dragonfly/sbin/svc/remote.c (revision ef2b2b9d)
1 /*
2  * Copyright (c) 2014 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 /*
35  * Handle remote listen/connect and parsing operations.
36  */
37 
38 #include "svc.h"
39 
40 typedef struct SvcConnect {
41 	struct SvcConnect *next;
42 	command_t *cmd;
43 	char	  *label;
44 	pthread_t td;
45 	int	active;
46 	int	fd;
47 	int	rc;
48 	FILE	*fpr;
49 	FILE	*fpw;
50 } connect_t;
51 
52 static void *remote_connect_thread(void *arg);
53 static void *remote_listener_thread(void *arg);
54 static void *remote_accepted_thread(void *arg);
55 static void remote_issue(connect_t *conn, command_t *cmd);
56 static int decode_args(connect_t *conn, char ***avp,
57 			const char *ptr, size_t len);
58 
59 connect_t *CHead;
60 connect_t **CNextP = &CHead;
61 
62 
63 /*
64  * Execute cmd on the running service by connecting to the service, passing-in
65  * the command, and processing results.
66  *
67  * Called only by master process
68  */
69 void
70 remote_execute(command_t *cmd, const char *label)
71 {
72 	connect_t *conn = calloc(sizeof(*conn), 1);
73 
74 	conn->fd = -1;
75 	conn->cmd = cmd;
76 	conn->label = strdup(label);
77 	conn->active = 1;
78 	conn->next = *CNextP;
79 	*CNextP = conn;
80 
81 	pthread_create(&conn->td, NULL, remote_connect_thread, conn);
82 }
83 
84 /*
85  * Threaded connect/execute
86  */
87 static
88 void *
89 remote_connect_thread(void *arg)
90 {
91 	connect_t *conn;
92 	command_t *cmd;
93 	struct sockaddr_un sou;
94 	size_t len;
95 	char *ptr;
96 
97 	conn = arg;
98 	cmd = conn->cmd;
99 
100 	bzero(&sou, sizeof(sou));
101 	if ((conn->fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0) {
102 		sou.sun_family = AF_UNIX;
103 		snprintf(sou.sun_path, sizeof(sou.sun_path),
104 			 "%s/service.%s.sk", cmd->piddir, conn->label);
105 		len = strlen(sou.sun_path);
106 		len = offsetof(struct sockaddr_un, sun_path[len+1]);
107 
108 		if (connect(conn->fd, (void *)&sou, len) < 0) {
109 			close(conn->fd);
110 			conn->fd = -1;
111 		}
112 	}
113 	if (conn->fd >= 0) {
114 		/*
115 		 * Issue command
116 		 */
117 		conn->fpr = fdopen(conn->fd, "r");
118 		conn->fpw = fdopen(dup(conn->fd), "w");
119 		conn->fd = -1;
120 		setvbuf(conn->fpr, NULL, _IOFBF, 0);
121 		setvbuf(conn->fpw, NULL, _IOFBF, 0);
122 		remote_issue(conn, cmd);
123 		while ((ptr = fgetln(conn->fpr, &len)) != NULL) {
124 			if (len == 2 && ptr[0] == '.' && ptr[1] == '\n')
125 				break;
126 
127 			/*
128 			 * de-escape ..
129 			 */
130 			if (len == 3 && ptr[0] == '.' && ptr[1] == '.' &&
131 			    ptr[2] == '\n') {
132 				++ptr;
133 				--len;
134 			}
135 			fwrite(ptr, 1, len, cmd->fp);
136 			fflush(cmd->fp);
137 		}
138 		conn->rc = 0;
139 		conn->active = 0;
140 		fclose(conn->fpr);
141 		fclose(conn->fpw);
142 		conn->fpr = NULL;
143 		conn->fpw = NULL;
144 	} else {
145 		/*
146 		 * Connection failed
147 		 */
148 		fprintf(cmd->fp,
149 			"Unable to connect to service %s\n",
150 			conn->label);
151 		conn->rc = 1;
152 		conn->active = 0;
153 		if (cmd->force_remove_files) {
154 			fprintf(cmd->fp,
155 				"Removing pid and socket files for %s\n",
156 				conn->label);
157 			remove_pid_and_socket(cmd, conn->label);
158 		}
159 	}
160 	return NULL;
161 }
162 
163 /*
164  * Called only by master process
165  *
166  * Collect status from all remote commands.
167  */
168 int
169 remote_wait(void)
170 {
171 	connect_t *scan;
172 	int rc = 0;
173 
174 	while ((scan = CHead) != NULL) {
175 		if (pthread_join(scan->td, NULL) < 0)
176 			continue;
177 		assert(scan->active == 0);
178 		rc += scan->rc;
179 		CHead = scan->next;
180 
181 		if (scan->fpr) {
182 			fclose(scan->fpr);
183 			scan->fpr = NULL;
184 		}
185 		if (scan->fpw) {
186 			fclose(scan->fpw);
187 			scan->fpw = NULL;
188 		}
189 		if (scan->fd >= 0) {
190 			close(scan->fd);
191 			scan->fd = -1;
192 		}
193 		if (scan->label) {
194 			free(scan->label);
195 			scan->label = NULL;
196 		}
197 		scan->cmd = NULL;
198 		free(scan);
199 	}
200 	return rc;
201 }
202 
203 /*
204  * Create the unix domain socket and pid file for the service
205  * and start a thread to accept and process connections.
206  *
207  * Return 0 on success, non-zero if the socket could not be created.
208  */
209 void
210 remote_listener(command_t *cmd, int lfd)
211 {
212 	connect_t *conn;
213 
214 	/*
215 	 * child, create our unix domain socket listener thread.
216 	 */
217 	conn = calloc(sizeof(*conn), 1);
218 	conn->fd = lfd;
219 	conn->cmd = cmd;
220 	conn->label = strdup(cmd->label);
221 	conn->active = 1;
222 
223 	conn->next = *CNextP;
224 	*CNextP = conn;
225 	pthread_create(&conn->td, NULL, remote_listener_thread, conn);
226 }
227 
228 static void *
229 remote_listener_thread(void *arg)
230 {
231 	connect_t *lconn = arg;
232 	connect_t *conn;
233 	struct sockaddr_un sou;
234 	socklen_t len;
235 
236 	conn = calloc(sizeof(*conn), 1);
237 	for (;;) {
238 		len = sizeof(sou);
239 		conn->fd = accept(lconn->fd, (void *)&sou, &len);
240 		conn->label = strdup(lconn->label);
241 		if (conn->fd < 0) {
242 			if (errno == EINTR)
243 				continue;
244 			break;
245 		}
246 		pthread_create(&conn->td, NULL, remote_accepted_thread, conn);
247 		conn = calloc(sizeof(*conn), 1);
248 	}
249 	free(conn);
250 
251 	return NULL;
252 }
253 
254 static void *
255 remote_accepted_thread(void *arg)
256 {
257 	connect_t *conn = arg;
258 	command_t cmd;
259 	char *ptr;
260 	size_t len;
261 	int rc;
262 	int ac;
263 	char **av;
264 
265 	pthread_detach(conn->td);
266 	conn->fpr = fdopen(conn->fd, "r");
267 	conn->fpw = fdopen(dup(conn->fd), "w");
268 	conn->fd = -1;
269 	setvbuf(conn->fpr, NULL, _IOFBF, 0);
270 	setvbuf(conn->fpw, NULL, _IOFBF, 0);
271 
272 	while ((ptr = fgetln(conn->fpr, &len)) != NULL) {
273 		ac = decode_args(conn, &av, ptr, len);
274 		rc = process_cmd(&cmd, conn->fpw, ac, av);
275 		cmd.cmdline = 0;	/* we are the remote */
276 		cmd.commanded = 1;	/* commanded action (vs automatic) */
277 		sreplace(&cmd.label, conn->label);
278 		if (rc == 0) {
279 			pthread_mutex_lock(&serial_mtx);
280 			rc = execute_cmd(&cmd);
281 			pthread_mutex_unlock(&serial_mtx);
282 		}
283 		free_cmd(&cmd);
284 		afree(&av);
285 		fwrite(".\n", 2, 1, conn->fpw);
286 		fflush(conn->fpw);
287 	}
288 	fclose(conn->fpr);
289 	fclose(conn->fpw);
290 	conn->fpr = NULL;
291 	conn->fpw = NULL;
292 	free(conn->label);
293 	free(conn);
294 
295 	return NULL;
296 }
297 
298 /*
299  * Issue the command to the remote, encode the arguments.
300  */
301 static
302 void
303 remote_issue(connect_t *conn, command_t *cmd)
304 {
305 	int i;
306 
307 	for (i = 1; i < cmd->orig_ac; ++i) {
308 		const char *str = cmd->orig_av[i];
309 
310 		if (i != 1)
311 			putc(' ', conn->fpw);
312 		while (*str) {
313 			if (*str == ' ' || *str == '\\' || *str == '\n')
314 				putc('\\', conn->fpw);
315 			putc(*str, conn->fpw);
316 			++str;
317 		}
318 	}
319 	putc('\n', conn->fpw);
320 	fflush(conn->fpw);
321 }
322 
323 /*
324  * Decode arguments
325  */
326 static int
327 decode_args(connect_t *conn __unused, char ***avp, const char *ptr, size_t len)
328 {
329 	char **av;
330 	char *arg;
331 	size_t i;
332 	size_t j;
333 	int acmax;
334 	int ac;
335 
336 	if (len && ptr[len-1] == '\n')
337 		--len;
338 
339 	acmax = 3;	/* av[0], first arg, terminating NULL */
340 	for (i = 0; i < len; ++i) {
341 		if (ptr[i] == ' ')
342 			++acmax;
343 	}
344 	av = calloc(sizeof(char *), acmax);
345 	av[0] = NULL;
346 	ac = 1;
347 
348 	i = 0;
349 	while (i < len) {
350 		for (j = i; j < len; ++j) {
351 			if (ptr[j] == ' ')
352 				break;
353 		}
354 		arg = malloc(j - i + 1);	/* worst case arg size */
355 		j = 0;
356 		while (i < len) {
357 			if (ptr[i] == ' ')
358 				break;
359 			if (ptr[i] == '\\' && i + 1 < len) {
360 				arg[j++] = ptr[i+1];
361 				i += 2;
362 			} else {
363 				arg[j++] = ptr[i];
364 				i += 1;
365 			}
366 		}
367 		arg[j] = 0;
368 		av[ac++] = arg;
369 		if (i < len && ptr[i] == ' ')
370 			++i;
371 	}
372 	av[ac] = NULL;
373 
374 #if 0
375 	fprintf(conn->fpw, "DECODE ARGS: ");
376 	for (i = 1; i < (size_t)ac; ++i)
377 		fprintf(conn->fpw, " \"%s\"", av[i]);
378 	fprintf(conn->fpw, "\n");
379 	fflush(conn->fpw);
380 #endif
381 
382 	*avp = av;
383 	return ac;
384 }
385