xref: /dflybsd-src/sbin/svc/remote.c (revision 82f395276fa6ba7906ecc95917af85f30a54d7a1)
13e2ea4ccSMatthew Dillon /*
23e2ea4ccSMatthew Dillon  * Copyright (c) 2014 The DragonFly Project.  All rights reserved.
33e2ea4ccSMatthew Dillon  *
43e2ea4ccSMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
53e2ea4ccSMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
63e2ea4ccSMatthew Dillon  *
73e2ea4ccSMatthew Dillon  * Redistribution and use in source and binary forms, with or without
83e2ea4ccSMatthew Dillon  * modification, are permitted provided that the following conditions
93e2ea4ccSMatthew Dillon  * are met:
103e2ea4ccSMatthew Dillon  *
113e2ea4ccSMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
123e2ea4ccSMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
133e2ea4ccSMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
143e2ea4ccSMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
153e2ea4ccSMatthew Dillon  *    the documentation and/or other materials provided with the
163e2ea4ccSMatthew Dillon  *    distribution.
173e2ea4ccSMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
183e2ea4ccSMatthew Dillon  *    contributors may be used to endorse or promote products derived
193e2ea4ccSMatthew Dillon  *    from this software without specific, prior written permission.
203e2ea4ccSMatthew Dillon  *
213e2ea4ccSMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
223e2ea4ccSMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
233e2ea4ccSMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
243e2ea4ccSMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
253e2ea4ccSMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
263e2ea4ccSMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
273e2ea4ccSMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
283e2ea4ccSMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
293e2ea4ccSMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
303e2ea4ccSMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
313e2ea4ccSMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
323e2ea4ccSMatthew Dillon  * SUCH DAMAGE.
333e2ea4ccSMatthew Dillon  */
343e2ea4ccSMatthew Dillon /*
353e2ea4ccSMatthew Dillon  * Handle remote listen/connect and parsing operations.
363e2ea4ccSMatthew Dillon  */
373e2ea4ccSMatthew Dillon 
383e2ea4ccSMatthew Dillon #include "svc.h"
393e2ea4ccSMatthew Dillon 
403e2ea4ccSMatthew Dillon typedef struct SvcConnect {
413e2ea4ccSMatthew Dillon 	struct SvcConnect *next;
423e2ea4ccSMatthew Dillon 	command_t *cmd;
433e2ea4ccSMatthew Dillon 	char	  *label;
443e2ea4ccSMatthew Dillon 	pthread_t td;
453e2ea4ccSMatthew Dillon 	int	active;
463e2ea4ccSMatthew Dillon 	int	fd;
473e2ea4ccSMatthew Dillon 	int	rc;
483e2ea4ccSMatthew Dillon 	FILE	*fpr;
493e2ea4ccSMatthew Dillon 	FILE	*fpw;
503e2ea4ccSMatthew Dillon } connect_t;
513e2ea4ccSMatthew Dillon 
523e2ea4ccSMatthew Dillon static void *remote_connect_thread(void *arg);
533e2ea4ccSMatthew Dillon static void *remote_listener_thread(void *arg);
543e2ea4ccSMatthew Dillon static void *remote_accepted_thread(void *arg);
553e2ea4ccSMatthew Dillon static void remote_issue(connect_t *conn, command_t *cmd);
563e2ea4ccSMatthew Dillon static int decode_args(connect_t *conn, char ***avp,
573e2ea4ccSMatthew Dillon 			const char *ptr, size_t len);
583e2ea4ccSMatthew Dillon 
593e2ea4ccSMatthew Dillon connect_t *CHead;
603e2ea4ccSMatthew Dillon connect_t **CNextP = &CHead;
613e2ea4ccSMatthew Dillon 
623e2ea4ccSMatthew Dillon 
633e2ea4ccSMatthew Dillon /*
643e2ea4ccSMatthew Dillon  * Execute cmd on the running service by connecting to the service, passing-in
653e2ea4ccSMatthew Dillon  * the command, and processing results.
663e2ea4ccSMatthew Dillon  *
673e2ea4ccSMatthew Dillon  * Called only by master process
683e2ea4ccSMatthew Dillon  */
693e2ea4ccSMatthew Dillon void
remote_execute(command_t * cmd,const char * label)703e2ea4ccSMatthew Dillon remote_execute(command_t *cmd, const char *label)
713e2ea4ccSMatthew Dillon {
723e2ea4ccSMatthew Dillon 	connect_t *conn = calloc(sizeof(*conn), 1);
733e2ea4ccSMatthew Dillon 
743e2ea4ccSMatthew Dillon 	conn->fd = -1;
753e2ea4ccSMatthew Dillon 	conn->cmd = cmd;
763e2ea4ccSMatthew Dillon 	conn->label = strdup(label);
773e2ea4ccSMatthew Dillon 	conn->active = 1;
783e2ea4ccSMatthew Dillon 	conn->next = *CNextP;
793e2ea4ccSMatthew Dillon 	*CNextP = conn;
803e2ea4ccSMatthew Dillon 
813e2ea4ccSMatthew Dillon 	pthread_create(&conn->td, NULL, remote_connect_thread, conn);
823e2ea4ccSMatthew Dillon }
833e2ea4ccSMatthew Dillon 
843e2ea4ccSMatthew Dillon /*
853e2ea4ccSMatthew Dillon  * Threaded connect/execute
863e2ea4ccSMatthew Dillon  */
873e2ea4ccSMatthew Dillon static
883e2ea4ccSMatthew Dillon void *
remote_connect_thread(void * arg)893e2ea4ccSMatthew Dillon remote_connect_thread(void *arg)
903e2ea4ccSMatthew Dillon {
913e2ea4ccSMatthew Dillon 	connect_t *conn;
923e2ea4ccSMatthew Dillon 	command_t *cmd;
933e2ea4ccSMatthew Dillon 	struct sockaddr_un sou;
943e2ea4ccSMatthew Dillon 	size_t len;
953e2ea4ccSMatthew Dillon 	char *ptr;
963e2ea4ccSMatthew Dillon 
973e2ea4ccSMatthew Dillon 	conn = arg;
983e2ea4ccSMatthew Dillon 	cmd = conn->cmd;
993e2ea4ccSMatthew Dillon 
1003e2ea4ccSMatthew Dillon 	bzero(&sou, sizeof(sou));
1013e2ea4ccSMatthew Dillon 	if ((conn->fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0) {
1023e2ea4ccSMatthew Dillon 		sou.sun_family = AF_UNIX;
1033e2ea4ccSMatthew Dillon 		snprintf(sou.sun_path, sizeof(sou.sun_path),
1043e2ea4ccSMatthew Dillon 			 "%s/service.%s.sk", cmd->piddir, conn->label);
1053e2ea4ccSMatthew Dillon 		len = strlen(sou.sun_path);
1063e2ea4ccSMatthew Dillon 		len = offsetof(struct sockaddr_un, sun_path[len+1]);
1073e2ea4ccSMatthew Dillon 
1083e2ea4ccSMatthew Dillon 		if (connect(conn->fd, (void *)&sou, len) < 0) {
1093e2ea4ccSMatthew Dillon 			close(conn->fd);
1103e2ea4ccSMatthew Dillon 			conn->fd = -1;
1113e2ea4ccSMatthew Dillon 		}
1123e2ea4ccSMatthew Dillon 	}
1133e2ea4ccSMatthew Dillon 	if (conn->fd >= 0) {
1143e2ea4ccSMatthew Dillon 		/*
1153e2ea4ccSMatthew Dillon 		 * Issue command
1163e2ea4ccSMatthew Dillon 		 */
1173e2ea4ccSMatthew Dillon 		conn->fpr = fdopen(conn->fd, "r");
1183e2ea4ccSMatthew Dillon 		conn->fpw = fdopen(dup(conn->fd), "w");
1193e2ea4ccSMatthew Dillon 		conn->fd = -1;
1203e2ea4ccSMatthew Dillon 		setvbuf(conn->fpr, NULL, _IOFBF, 0);
1213e2ea4ccSMatthew Dillon 		setvbuf(conn->fpw, NULL, _IOFBF, 0);
1223e2ea4ccSMatthew Dillon 		remote_issue(conn, cmd);
1233e2ea4ccSMatthew Dillon 		while ((ptr = fgetln(conn->fpr, &len)) != NULL) {
1243e2ea4ccSMatthew Dillon 			if (len == 2 && ptr[0] == '.' && ptr[1] == '\n')
1253e2ea4ccSMatthew Dillon 				break;
126*82f39527SMatthew Dillon 
127*82f39527SMatthew Dillon 			/*
128*82f39527SMatthew Dillon 			 * de-escape ..
129*82f39527SMatthew Dillon 			 */
130*82f39527SMatthew Dillon 			if (len == 3 && ptr[0] == '.' && ptr[1] == '.' &&
131*82f39527SMatthew Dillon 			    ptr[2] == '\n') {
132*82f39527SMatthew Dillon 				++ptr;
133*82f39527SMatthew Dillon 				--len;
134*82f39527SMatthew Dillon 			}
1353e2ea4ccSMatthew Dillon 			fwrite(ptr, 1, len, cmd->fp);
1363e2ea4ccSMatthew Dillon 			fflush(cmd->fp);
1373e2ea4ccSMatthew Dillon 		}
1383e2ea4ccSMatthew Dillon 		conn->rc = 0;
1393e2ea4ccSMatthew Dillon 		conn->active = 0;
1403e2ea4ccSMatthew Dillon 		fclose(conn->fpr);
1413e2ea4ccSMatthew Dillon 		fclose(conn->fpw);
1423e2ea4ccSMatthew Dillon 		conn->fpr = NULL;
1433e2ea4ccSMatthew Dillon 		conn->fpw = NULL;
1443e2ea4ccSMatthew Dillon 	} else {
1453e2ea4ccSMatthew Dillon 		/*
1463e2ea4ccSMatthew Dillon 		 * Connection failed
1473e2ea4ccSMatthew Dillon 		 */
1483e2ea4ccSMatthew Dillon 		fprintf(cmd->fp,
1493e2ea4ccSMatthew Dillon 			"Unable to connect to service %s\n",
1503e2ea4ccSMatthew Dillon 			conn->label);
1513e2ea4ccSMatthew Dillon 		conn->rc = 1;
1523e2ea4ccSMatthew Dillon 		conn->active = 0;
153ee6361d4SMatthew Dillon 		if (cmd->force_remove_files) {
154ee6361d4SMatthew Dillon 			fprintf(cmd->fp,
155ee6361d4SMatthew Dillon 				"Removing pid and socket files for %s\n",
156ee6361d4SMatthew Dillon 				conn->label);
157ee6361d4SMatthew Dillon 			remove_pid_and_socket(cmd, conn->label);
158ee6361d4SMatthew Dillon 		}
1593e2ea4ccSMatthew Dillon 	}
1603e2ea4ccSMatthew Dillon 	return NULL;
1613e2ea4ccSMatthew Dillon }
1623e2ea4ccSMatthew Dillon 
1633e2ea4ccSMatthew Dillon /*
1643e2ea4ccSMatthew Dillon  * Called only by master process
1653e2ea4ccSMatthew Dillon  *
1663e2ea4ccSMatthew Dillon  * Collect status from all remote commands.
1673e2ea4ccSMatthew Dillon  */
1683e2ea4ccSMatthew Dillon int
remote_wait(void)1693e2ea4ccSMatthew Dillon remote_wait(void)
1703e2ea4ccSMatthew Dillon {
1713e2ea4ccSMatthew Dillon 	connect_t *scan;
1723e2ea4ccSMatthew Dillon 	int rc = 0;
1733e2ea4ccSMatthew Dillon 
1743e2ea4ccSMatthew Dillon 	while ((scan = CHead) != NULL) {
1753e2ea4ccSMatthew Dillon 		if (pthread_join(scan->td, NULL) < 0)
1763e2ea4ccSMatthew Dillon 			continue;
1773e2ea4ccSMatthew Dillon 		assert(scan->active == 0);
1783e2ea4ccSMatthew Dillon 		rc += scan->rc;
1793e2ea4ccSMatthew Dillon 		CHead = scan->next;
1803e2ea4ccSMatthew Dillon 
1813e2ea4ccSMatthew Dillon 		if (scan->fpr) {
1823e2ea4ccSMatthew Dillon 			fclose(scan->fpr);
1833e2ea4ccSMatthew Dillon 			scan->fpr = NULL;
1843e2ea4ccSMatthew Dillon 		}
1853e2ea4ccSMatthew Dillon 		if (scan->fpw) {
1863e2ea4ccSMatthew Dillon 			fclose(scan->fpw);
1873e2ea4ccSMatthew Dillon 			scan->fpw = NULL;
1883e2ea4ccSMatthew Dillon 		}
1893e2ea4ccSMatthew Dillon 		if (scan->fd >= 0) {
1903e2ea4ccSMatthew Dillon 			close(scan->fd);
1913e2ea4ccSMatthew Dillon 			scan->fd = -1;
1923e2ea4ccSMatthew Dillon 		}
1933e2ea4ccSMatthew Dillon 		if (scan->label) {
1943e2ea4ccSMatthew Dillon 			free(scan->label);
1953e2ea4ccSMatthew Dillon 			scan->label = NULL;
1963e2ea4ccSMatthew Dillon 		}
1973e2ea4ccSMatthew Dillon 		scan->cmd = NULL;
1983e2ea4ccSMatthew Dillon 		free(scan);
1993e2ea4ccSMatthew Dillon 	}
2003e2ea4ccSMatthew Dillon 	return rc;
2013e2ea4ccSMatthew Dillon }
2023e2ea4ccSMatthew Dillon 
2033e2ea4ccSMatthew Dillon /*
2043e2ea4ccSMatthew Dillon  * Create the unix domain socket and pid file for the service
2053e2ea4ccSMatthew Dillon  * and start a thread to accept and process connections.
2063e2ea4ccSMatthew Dillon  *
2073e2ea4ccSMatthew Dillon  * Return 0 on success, non-zero if the socket could not be created.
2083e2ea4ccSMatthew Dillon  */
2093e2ea4ccSMatthew Dillon void
remote_listener(command_t * cmd,int lfd)2103e2ea4ccSMatthew Dillon remote_listener(command_t *cmd, int lfd)
2113e2ea4ccSMatthew Dillon {
2123e2ea4ccSMatthew Dillon 	connect_t *conn;
2133e2ea4ccSMatthew Dillon 
2143e2ea4ccSMatthew Dillon 	/*
2153e2ea4ccSMatthew Dillon 	 * child, create our unix domain socket listener thread.
2163e2ea4ccSMatthew Dillon 	 */
2173e2ea4ccSMatthew Dillon 	conn = calloc(sizeof(*conn), 1);
2183e2ea4ccSMatthew Dillon 	conn->fd = lfd;
2193e2ea4ccSMatthew Dillon 	conn->cmd = cmd;
2203e2ea4ccSMatthew Dillon 	conn->label = strdup(cmd->label);
2213e2ea4ccSMatthew Dillon 	conn->active = 1;
2223e2ea4ccSMatthew Dillon 
2233e2ea4ccSMatthew Dillon 	conn->next = *CNextP;
2243e2ea4ccSMatthew Dillon 	*CNextP = conn;
2253e2ea4ccSMatthew Dillon 	pthread_create(&conn->td, NULL, remote_listener_thread, conn);
2263e2ea4ccSMatthew Dillon }
2273e2ea4ccSMatthew Dillon 
2283e2ea4ccSMatthew Dillon static void *
remote_listener_thread(void * arg)2293e2ea4ccSMatthew Dillon remote_listener_thread(void *arg)
2303e2ea4ccSMatthew Dillon {
2313e2ea4ccSMatthew Dillon 	connect_t *lconn = arg;
2323e2ea4ccSMatthew Dillon 	connect_t *conn;
2333e2ea4ccSMatthew Dillon 	struct sockaddr_un sou;
2343e2ea4ccSMatthew Dillon 	socklen_t len;
2353e2ea4ccSMatthew Dillon 
2363e2ea4ccSMatthew Dillon 	conn = calloc(sizeof(*conn), 1);
2373e2ea4ccSMatthew Dillon 	for (;;) {
2383e2ea4ccSMatthew Dillon 		len = sizeof(sou);
2393e2ea4ccSMatthew Dillon 		conn->fd = accept(lconn->fd, (void *)&sou, &len);
2403e2ea4ccSMatthew Dillon 		conn->label = strdup(lconn->label);
2413e2ea4ccSMatthew Dillon 		if (conn->fd < 0) {
2423e2ea4ccSMatthew Dillon 			if (errno == EINTR)
2433e2ea4ccSMatthew Dillon 				continue;
2443e2ea4ccSMatthew Dillon 			break;
2453e2ea4ccSMatthew Dillon 		}
2463e2ea4ccSMatthew Dillon 		pthread_create(&conn->td, NULL, remote_accepted_thread, conn);
2473e2ea4ccSMatthew Dillon 		conn = calloc(sizeof(*conn), 1);
2483e2ea4ccSMatthew Dillon 	}
2493e2ea4ccSMatthew Dillon 	free(conn);
2503e2ea4ccSMatthew Dillon 
2513e2ea4ccSMatthew Dillon 	return NULL;
2523e2ea4ccSMatthew Dillon }
2533e2ea4ccSMatthew Dillon 
2543e2ea4ccSMatthew Dillon static void *
remote_accepted_thread(void * arg)2553e2ea4ccSMatthew Dillon remote_accepted_thread(void *arg)
2563e2ea4ccSMatthew Dillon {
2573e2ea4ccSMatthew Dillon 	connect_t *conn = arg;
2583e2ea4ccSMatthew Dillon 	command_t cmd;
2593e2ea4ccSMatthew Dillon 	char *ptr;
2603e2ea4ccSMatthew Dillon 	size_t len;
2613e2ea4ccSMatthew Dillon 	int rc;
2623e2ea4ccSMatthew Dillon 	int ac;
2633e2ea4ccSMatthew Dillon 	char **av;
2643e2ea4ccSMatthew Dillon 
2653e2ea4ccSMatthew Dillon 	pthread_detach(conn->td);
2663e2ea4ccSMatthew Dillon 	conn->fpr = fdopen(conn->fd, "r");
2673e2ea4ccSMatthew Dillon 	conn->fpw = fdopen(dup(conn->fd), "w");
2683e2ea4ccSMatthew Dillon 	conn->fd = -1;
2693e2ea4ccSMatthew Dillon 	setvbuf(conn->fpr, NULL, _IOFBF, 0);
2703e2ea4ccSMatthew Dillon 	setvbuf(conn->fpw, NULL, _IOFBF, 0);
2713e2ea4ccSMatthew Dillon 
2723e2ea4ccSMatthew Dillon 	while ((ptr = fgetln(conn->fpr, &len)) != NULL) {
2733e2ea4ccSMatthew Dillon 		ac = decode_args(conn, &av, ptr, len);
2743e2ea4ccSMatthew Dillon 		rc = process_cmd(&cmd, conn->fpw, ac, av);
2753e2ea4ccSMatthew Dillon 		cmd.cmdline = 0;	/* we are the remote */
2763e2ea4ccSMatthew Dillon 		cmd.commanded = 1;	/* commanded action (vs automatic) */
2773e2ea4ccSMatthew Dillon 		sreplace(&cmd.label, conn->label);
2783e2ea4ccSMatthew Dillon 		if (rc == 0) {
2793e2ea4ccSMatthew Dillon 			pthread_mutex_lock(&serial_mtx);
2803e2ea4ccSMatthew Dillon 			rc = execute_cmd(&cmd);
2813e2ea4ccSMatthew Dillon 			pthread_mutex_unlock(&serial_mtx);
2823e2ea4ccSMatthew Dillon 		}
2833e2ea4ccSMatthew Dillon 		free_cmd(&cmd);
2843e2ea4ccSMatthew Dillon 		afree(&av);
2853e2ea4ccSMatthew Dillon 		fwrite(".\n", 2, 1, conn->fpw);
2863e2ea4ccSMatthew Dillon 		fflush(conn->fpw);
2873e2ea4ccSMatthew Dillon 	}
2883e2ea4ccSMatthew Dillon 	fclose(conn->fpr);
2893e2ea4ccSMatthew Dillon 	fclose(conn->fpw);
2903e2ea4ccSMatthew Dillon 	conn->fpr = NULL;
2913e2ea4ccSMatthew Dillon 	conn->fpw = NULL;
2923e2ea4ccSMatthew Dillon 	free(conn->label);
2933e2ea4ccSMatthew Dillon 	free(conn);
2943e2ea4ccSMatthew Dillon 
2953e2ea4ccSMatthew Dillon 	return NULL;
2963e2ea4ccSMatthew Dillon }
2973e2ea4ccSMatthew Dillon 
2983e2ea4ccSMatthew Dillon /*
2993e2ea4ccSMatthew Dillon  * Issue the command to the remote, encode the arguments.
3003e2ea4ccSMatthew Dillon  */
3013e2ea4ccSMatthew Dillon static
3023e2ea4ccSMatthew Dillon void
remote_issue(connect_t * conn,command_t * cmd)3033e2ea4ccSMatthew Dillon remote_issue(connect_t *conn, command_t *cmd)
3043e2ea4ccSMatthew Dillon {
3053e2ea4ccSMatthew Dillon 	int i;
3063e2ea4ccSMatthew Dillon 
3073e2ea4ccSMatthew Dillon 	for (i = 1; i < cmd->orig_ac; ++i) {
3083e2ea4ccSMatthew Dillon 		const char *str = cmd->orig_av[i];
3093e2ea4ccSMatthew Dillon 
3103e2ea4ccSMatthew Dillon 		if (i != 1)
3113e2ea4ccSMatthew Dillon 			putc(' ', conn->fpw);
3123e2ea4ccSMatthew Dillon 		while (*str) {
3133e2ea4ccSMatthew Dillon 			if (*str == ' ' || *str == '\\' || *str == '\n')
3143e2ea4ccSMatthew Dillon 				putc('\\', conn->fpw);
3153e2ea4ccSMatthew Dillon 			putc(*str, conn->fpw);
3163e2ea4ccSMatthew Dillon 			++str;
3173e2ea4ccSMatthew Dillon 		}
3183e2ea4ccSMatthew Dillon 	}
3193e2ea4ccSMatthew Dillon 	putc('\n', conn->fpw);
3203e2ea4ccSMatthew Dillon 	fflush(conn->fpw);
3213e2ea4ccSMatthew Dillon }
3223e2ea4ccSMatthew Dillon 
3233e2ea4ccSMatthew Dillon /*
3243e2ea4ccSMatthew Dillon  * Decode arguments
3253e2ea4ccSMatthew Dillon  */
3263e2ea4ccSMatthew Dillon static int
decode_args(connect_t * conn __unused,char *** avp,const char * ptr,size_t len)3273e2ea4ccSMatthew Dillon decode_args(connect_t *conn __unused, char ***avp, const char *ptr, size_t len)
3283e2ea4ccSMatthew Dillon {
3293e2ea4ccSMatthew Dillon 	char **av;
3303e2ea4ccSMatthew Dillon 	char *arg;
3313e2ea4ccSMatthew Dillon 	size_t i;
3323e2ea4ccSMatthew Dillon 	size_t j;
3333e2ea4ccSMatthew Dillon 	int acmax;
3343e2ea4ccSMatthew Dillon 	int ac;
3353e2ea4ccSMatthew Dillon 
3363e2ea4ccSMatthew Dillon 	if (len && ptr[len-1] == '\n')
3373e2ea4ccSMatthew Dillon 		--len;
3383e2ea4ccSMatthew Dillon 
3393e2ea4ccSMatthew Dillon 	acmax = 3;	/* av[0], first arg, terminating NULL */
3403e2ea4ccSMatthew Dillon 	for (i = 0; i < len; ++i) {
3413e2ea4ccSMatthew Dillon 		if (ptr[i] == ' ')
3423e2ea4ccSMatthew Dillon 			++acmax;
3433e2ea4ccSMatthew Dillon 	}
3443e2ea4ccSMatthew Dillon 	av = calloc(sizeof(char *), acmax);
3453e2ea4ccSMatthew Dillon 	av[0] = NULL;
3463e2ea4ccSMatthew Dillon 	ac = 1;
3473e2ea4ccSMatthew Dillon 
3483e2ea4ccSMatthew Dillon 	i = 0;
3493e2ea4ccSMatthew Dillon 	while (i < len) {
3503e2ea4ccSMatthew Dillon 		for (j = i; j < len; ++j) {
3513e2ea4ccSMatthew Dillon 			if (ptr[j] == ' ')
3523e2ea4ccSMatthew Dillon 				break;
3533e2ea4ccSMatthew Dillon 		}
3543e2ea4ccSMatthew Dillon 		arg = malloc(j - i + 1);	/* worst case arg size */
3553e2ea4ccSMatthew Dillon 		j = 0;
3563e2ea4ccSMatthew Dillon 		while (i < len) {
3573e2ea4ccSMatthew Dillon 			if (ptr[i] == ' ')
3583e2ea4ccSMatthew Dillon 				break;
3593e2ea4ccSMatthew Dillon 			if (ptr[i] == '\\' && i + 1 < len) {
3603e2ea4ccSMatthew Dillon 				arg[j++] = ptr[i+1];
3613e2ea4ccSMatthew Dillon 				i += 2;
3623e2ea4ccSMatthew Dillon 			} else {
3633e2ea4ccSMatthew Dillon 				arg[j++] = ptr[i];
3643e2ea4ccSMatthew Dillon 				i += 1;
3653e2ea4ccSMatthew Dillon 			}
3663e2ea4ccSMatthew Dillon 		}
3673e2ea4ccSMatthew Dillon 		arg[j] = 0;
3683e2ea4ccSMatthew Dillon 		av[ac++] = arg;
3693e2ea4ccSMatthew Dillon 		if (i < len && ptr[i] == ' ')
3703e2ea4ccSMatthew Dillon 			++i;
3713e2ea4ccSMatthew Dillon 	}
3723e2ea4ccSMatthew Dillon 	av[ac] = NULL;
3733e2ea4ccSMatthew Dillon 
3743e2ea4ccSMatthew Dillon #if 0
3753e2ea4ccSMatthew Dillon 	fprintf(conn->fpw, "DECODE ARGS: ");
3763e2ea4ccSMatthew Dillon 	for (i = 1; i < (size_t)ac; ++i)
3773e2ea4ccSMatthew Dillon 		fprintf(conn->fpw, " \"%s\"", av[i]);
3783e2ea4ccSMatthew Dillon 	fprintf(conn->fpw, "\n");
3793e2ea4ccSMatthew Dillon 	fflush(conn->fpw);
3803e2ea4ccSMatthew Dillon #endif
3813e2ea4ccSMatthew Dillon 
3823e2ea4ccSMatthew Dillon 	*avp = av;
3833e2ea4ccSMatthew Dillon 	return ac;
3843e2ea4ccSMatthew Dillon }
385