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: */
38 char *child_args; /* Arguments to child when -C is used */
39
40 static char *start_cwd; /* Stores the starting directory for self_exec */
41
asprintf(char ** app,const char * fmt,...)42 int 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
maybe_run_child(void (* child)(),const char * fmt,...)71 void 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
self_exec(const char * argv0,const char * fmt,...)164 int 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