1/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: t -*- */
2/*
3 * self_exec.c: self_exec magic required to run child functions on uClinux
4 *
5 * Copyright (C) 2005 Paul J.Y. Lahaie <pjlahaie-at-steamballoon.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 *
21 * This software was produced by Steamballoon Incorporated
22 * 55 Byward Market Square, 2nd Floor North, Ottawa, ON K1N 9C3, Canada
23 */
24
25#define _GNU_SOURCE		/* for asprintf */
26
27#include "config.h"
28
29#ifdef UCLINUX
30
31#include <stdarg.h>
32#include <string.h>
33#include <stdio.h>
34#include "test.h"
35#include "safe_macros.h"
36
37/* Set from parse_opts.c: */
38char *child_args;		/* Arguments to child when -C is used */
39
40static char *start_cwd;		/* Stores the starting directory for self_exec */
41
42int asprintf(char **app, const char *fmt, ...)
43{
44	va_list ptr;
45	int rv;
46	char *p;
47
48	/*
49	 * First iteration - find out size of buffer required and allocate it.
50	 */
51	va_start(ptr, fmt);
52	rv = vsnprintf(NULL, 0, fmt, ptr);
53	va_end(ptr);
54
55	p = malloc(++rv);	/* allocate the buffer */
56	*app = p;
57	if (!p) {
58		return -1;
59	}
60
61	/*
62	 * Second iteration - actually produce output.
63	 */
64	va_start(ptr, fmt);
65	rv = vsnprintf(p, rv, fmt, ptr);
66	va_end(ptr);
67
68	return rv;
69}
70
71void maybe_run_child(void (*child) (), const char *fmt, ...)
72{
73	va_list ap;
74	char *child_dir;
75	char *p, *tok;
76	int *iptr, i, j;
77	char *s;
78	char **sptr;
79	char *endptr;
80
81	/* Store the current directory for later use. */
82	start_cwd = getcwd(NULL, 0);
83
84	if (child_args) {
85		char *args = strdup(child_args);
86
87		child_dir = strtok(args, ",");
88		if (strlen(child_dir) == 0) {
89			tst_brkm(TBROK, NULL,
90				 "Could not get directory from -C option");
91			return;
92		}
93
94		va_start(ap, fmt);
95
96		for (p = fmt; *p; p++) {
97			tok = strtok(NULL, ",");
98			if (!tok || strlen(tok) == 0) {
99				tst_brkm(TBROK, NULL,
100					 "Invalid argument to -C option");
101				return;
102			}
103
104			switch (*p) {
105			case 'd':
106				iptr = va_arg(ap, int *);
107				i = strtol(tok, &endptr, 10);
108				if (*endptr != '\0') {
109					tst_brkm(TBROK, NULL,
110						 "Invalid argument to -C option");
111					return;
112				}
113				*iptr = i;
114				break;
115			case 'n':
116				j = va_arg(ap, int);
117				i = strtol(tok, &endptr, 10);
118				if (*endptr != '\0') {
119					tst_brkm(TBROK, NULL,
120						 "Invalid argument to -C option");
121					return;
122				}
123				if (j != i) {
124					va_end(ap);
125					free(args);
126					return;
127				}
128				break;
129			case 's':
130				s = va_arg(ap, char *);
131				if (!strncpy(s, tok, strlen(tok) + 1)) {
132					tst_brkm(TBROK, NULL,
133						 "Could not strncpy for -C option");
134					return;
135				}
136				break;
137			case 'S':
138				sptr = va_arg(ap, char **);
139				*sptr = strdup(tok);
140				if (!*sptr) {
141					tst_brkm(TBROK, NULL,
142						 "Could not strdup for -C option");
143					return;
144				}
145				break;
146			default:
147				tst_brkm(TBROK, NULL,
148					 "Format string option %c not implemented",
149					 *p);
150				return;
151			}
152		}
153
154		va_end(ap);
155		free(args);
156		SAFE_CHDIR(NULL, child_dir);
157
158		(*child) ();
159		tst_resm(TWARN, "Child function returned unexpectedly");
160		/* Exit here? or exit silently? */
161	}
162}
163
164int self_exec(const char *argv0, const char *fmt, ...)
165{
166	va_list ap;
167	char *p;
168	char *tmp_cwd;
169	char *arg;
170	int ival;
171	char *str;
172
173	if ((tmp_cwd = getcwd(NULL, 0)) == NULL) {
174		tst_resm(TBROK, "Could not getcwd()");
175		return -1;
176	}
177
178	arg = strdup(tmp_cwd);
179	if (arg == NULL) {
180		tst_resm(TBROK, "Could not produce self_exec string");
181		return -1;
182	}
183
184	va_start(ap, fmt);
185
186	for (p = fmt; *p; p++) {
187		switch (*p) {
188		case 'd':
189		case 'n':
190			ival = va_arg(ap, int);
191			if (asprintf(&arg, "%s,%d", arg, ival) < 0) {
192				tst_resm(TBROK,
193					 "Could not produce self_exec string");
194				return -1;
195			}
196			break;
197		case 's':
198		case 'S':
199			str = va_arg(ap, char *);
200			if (asprintf(&arg, "%s,%s", arg, str) < 0) {
201				tst_resm(TBROK,
202					 "Could not produce self_exec string");
203				return -1;
204			}
205			break;
206		default:
207			tst_resm(TBROK,
208				 "Format string option %c not implemented", *p);
209			return -1;
210			break;
211		}
212	}
213
214	va_end(ap);
215
216	if (chdir(start_cwd) < 0) {
217		tst_resm(TBROK, "Could not change to %s for self_exec",
218			 start_cwd);
219		return -1;
220	}
221
222	return execlp(argv0, argv0, "-C", arg, (char *)NULL);
223}
224
225#endif /* UCLINUX */
226