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
36/* Set from parse_opts.c: */
37char *child_args;		/* Arguments to child when -C is used */
38
39static char *start_cwd;		/* Stores the starting directory for self_exec */
40
41int asprintf(char **app, const char *fmt, ...)
42{
43	va_list ptr;
44	int rv;
45	char *p;
46
47	/*
48	 * First iteration - find out size of buffer required and allocate it.
49	 */
50	va_start(ptr, fmt);
51	rv = vsnprintf(NULL, 0, fmt, ptr);
52	va_end(ptr);
53
54	p = malloc(++rv);	/* allocate the buffer */
55	*app = p;
56	if (!p) {
57		return -1;
58	}
59
60	/*
61	 * Second iteration - actually produce output.
62	 */
63	va_start(ptr, fmt);
64	rv = vsnprintf(p, rv, fmt, ptr);
65	va_end(ptr);
66
67	return rv;
68}
69
70void maybe_run_child(void (*child) (), const char *fmt, ...)
71{
72	va_list ap;
73	char *child_dir;
74	char *p, *tok;
75	int *iptr, i, j;
76	char *s;
77	char **sptr;
78	char *endptr;
79
80	/* Store the current directory for later use. */
81	start_cwd = getcwd(NULL, 0);
82
83	if (child_args) {
84		char *args = strdup(child_args);
85
86		child_dir = strtok(args, ",");
87		if (strlen(child_dir) == 0) {
88			tst_brkm(TBROK, NULL,
89				 "Could not get directory from -C option");
90			return;
91		}
92
93		va_start(ap, fmt);
94
95		for (p = fmt; *p; p++) {
96			tok = strtok(NULL, ",");
97			if (!tok || strlen(tok) == 0) {
98				tst_brkm(TBROK, NULL,
99					 "Invalid argument to -C option");
100				return;
101			}
102
103			switch (*p) {
104			case 'd':
105				iptr = va_arg(ap, int *);
106				i = strtol(tok, &endptr, 10);
107				if (*endptr != '\0') {
108					tst_brkm(TBROK, NULL,
109						 "Invalid argument to -C option");
110					return;
111				}
112				*iptr = i;
113				break;
114			case 'n':
115				j = va_arg(ap, int);
116				i = strtol(tok, &endptr, 10);
117				if (*endptr != '\0') {
118					tst_brkm(TBROK, NULL,
119						 "Invalid argument to -C option");
120					return;
121				}
122				if (j != i) {
123					va_end(ap);
124					free(args);
125					return;
126				}
127				break;
128			case 's':
129				s = va_arg(ap, char *);
130				if (!strncpy(s, tok, strlen(tok) + 1)) {
131					tst_brkm(TBROK, NULL,
132						 "Could not strncpy for -C option");
133					return;
134				}
135				break;
136			case 'S':
137				sptr = va_arg(ap, char **);
138				*sptr = strdup(tok);
139				if (!*sptr) {
140					tst_brkm(TBROK, NULL,
141						 "Could not strdup for -C option");
142					return;
143				}
144				break;
145			default:
146				tst_brkm(TBROK, NULL,
147					 "Format string option %c not implemented",
148					 *p);
149				return;
150			}
151		}
152
153		va_end(ap);
154		free(args);
155		if (chdir(child_dir) < 0) {
156			tst_brkm(TBROK, NULL,
157				 "Could not change to %s for child", child_dir);
158			return;
159		}
160
161		(*child) ();
162		tst_resm(TWARN, "Child function returned unexpectedly");
163		/* Exit here? or exit silently? */
164	}
165}
166
167int self_exec(const char *argv0, const char *fmt, ...)
168{
169	va_list ap;
170	char *p;
171	char *tmp_cwd;
172	char *arg;
173	int ival;
174	char *str;
175
176	if ((tmp_cwd = getcwd(NULL, 0)) == NULL) {
177		tst_resm(TBROK, "Could not getcwd()");
178		return -1;
179	}
180
181	arg = strdup(tmp_cwd);
182	if (arg == NULL) {
183		tst_resm(TBROK, "Could not produce self_exec string");
184		return -1;
185	}
186
187	va_start(ap, fmt);
188
189	for (p = fmt; *p; p++) {
190		switch (*p) {
191		case 'd':
192		case 'n':
193			ival = va_arg(ap, int);
194			if (asprintf(&arg, "%s,%d", arg, ival) < 0) {
195				tst_resm(TBROK,
196					 "Could not produce self_exec string");
197				return -1;
198			}
199			break;
200		case 's':
201		case 'S':
202			str = va_arg(ap, char *);
203			if (asprintf(&arg, "%s,%s", arg, str) < 0) {
204				tst_resm(TBROK,
205					 "Could not produce self_exec string");
206				return -1;
207			}
208			break;
209		default:
210			tst_resm(TBROK,
211				 "Format string option %c not implemented", *p);
212			return -1;
213			break;
214		}
215	}
216
217	va_end(ap);
218
219	if (chdir(start_cwd) < 0) {
220		tst_resm(TBROK, "Could not change to %s for self_exec",
221			 start_cwd);
222		return -1;
223	}
224
225	return execlp(argv0, argv0, "-C", arg, (char *)NULL);
226}
227
228#endif /* UCLINUX */
229