/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: t -*- */ | |
/* | |
* self_exec.c: self_exec magic required to run child functions on uClinux | |
* | |
* Copyright (C) 2005 Paul J.Y. Lahaie <pjlahaie-at-steamballoon.com> | |
* | |
* This program is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU General Public License | |
* as published by the Free Software Foundation; either version 2 | |
* of the License, or (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program; if not, write to the Free Software | |
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
* | |
* This software was produced by Steamballoon Incorporated | |
* 55 Byward Market Square, 2nd Floor North, Ottawa, ON K1N 9C3, Canada | |
*/ | |
#ifdef UCLINUX | |
#define _GNU_SOURCE /* for asprintf */ | |
#include <stdarg.h> | |
#include <string.h> | |
#include <stdio.h> | |
#include "test.h" | |
/* Set from parse_opts.c: */ | |
char *child_args; /* Arguments to child when -C is used */ | |
static char *start_cwd; /* Stores the starting directory for self_exec */ | |
int asprintf(char **app, const char *fmt, ...) | |
{ | |
va_list ptr; | |
int rv; | |
char *p; | |
/* | |
* First iteration - find out size of buffer required and allocate it. | |
*/ | |
va_start(ptr, fmt); | |
rv = vsnprintf(NULL, 0, fmt, ptr); | |
va_end(ptr); | |
p = malloc(++rv); /* allocate the buffer */ | |
*app = p; | |
if (!p) { | |
return -1; | |
} | |
/* | |
* Second iteration - actually produce output. | |
*/ | |
va_start(ptr, fmt); | |
rv = vsnprintf(p, rv, fmt, ptr); | |
va_end(ptr); | |
return rv; | |
} | |
void | |
maybe_run_child(void (*child)(), char *fmt, ...) | |
{ | |
va_list ap; | |
char *child_dir; | |
char *p, *tok; | |
int *iptr, i, j; | |
char *s; | |
char **sptr; | |
char *endptr; | |
/* Store the current directory for later use. */ | |
start_cwd = getcwd(NULL, 0); | |
if (child_args) { | |
char *args = strdup(child_args); | |
child_dir = strtok(args, ","); | |
if (strlen(child_dir) == 0) { | |
tst_resm(TBROK, NULL, "Could not get directory from -C option"); | |
tst_exit(); | |
} | |
va_start(ap, fmt); | |
for (p = fmt; *p; p++) { | |
tok = strtok(NULL, ","); | |
if (!tok || strlen(tok) == 0) { | |
tst_resm(TBROK, "Invalid argument to -C option"); | |
tst_exit(); | |
} | |
switch (*p) { | |
case 'd': | |
iptr = va_arg(ap, int *); | |
i = strtol(tok, &endptr, 10); | |
if (*endptr != '\0') { | |
tst_resm(TBROK, "Invalid argument to -C option"); | |
tst_exit(); | |
} | |
*iptr = i; | |
break; | |
case 'n': | |
j = va_arg(ap, int); | |
i = strtol(tok, &endptr, 10); | |
if (*endptr != '\0') { | |
tst_resm(TBROK, "Invalid argument to -C option"); | |
tst_exit(); | |
} | |
if (j != i) { | |
va_end(ap); | |
return; | |
} | |
break; | |
case 's': | |
s = va_arg(ap, char *); | |
if (!strncpy(s, tok, strlen(tok)+1)) { | |
tst_resm(TBROK, "Could not strncpy for -C option"); | |
tst_exit(); | |
} | |
break; | |
case 'S': | |
sptr = va_arg(ap, char **); | |
*sptr = strdup(tok); | |
if (!*sptr) { | |
tst_resm(TBROK, "Could not strdup for -C option"); | |
tst_exit(); | |
} | |
break; | |
default: | |
tst_resm(TBROK, "Format string option %c not implemented", *p); | |
tst_exit(); | |
break; | |
} | |
} | |
va_end(ap); | |
if (chdir(child_dir) < 0) { | |
tst_resm(TBROK, "Could not change to %s for child", child_dir); | |
tst_exit(); | |
} | |
(*child)(); | |
tst_resm(TWARN, "Child function returned unexpectedly"); | |
/* Exit here? or exit silently? */ | |
} | |
} | |
int | |
self_exec(char *argv0, char *fmt, ...) | |
{ | |
va_list ap; | |
char *p; | |
char *tmp_cwd; | |
char *arg; | |
int ival; | |
char *str; | |
if ((tmp_cwd = getcwd(NULL, 0)) == NULL) { | |
tst_resm(TBROK, "Could not getcwd()"); | |
return -1; | |
} | |
arg = strdup( tmp_cwd ); | |
if (( arg = strdup( tmp_cwd )) == NULL) { | |
tst_resm(TBROK, "Could not produce self_exec string"); | |
return -1; | |
} | |
va_start(ap, fmt); | |
for (p = fmt; *p; p++) { | |
switch (*p) { | |
case 'd': | |
case 'n': | |
ival = va_arg(ap, int); | |
if (asprintf(&arg, "%s,%d", arg, ival) < 0) { | |
tst_resm(TBROK, "Could not produce self_exec string"); | |
return -1; | |
} | |
break; | |
case 's': | |
case 'S': | |
str = va_arg(ap, char *); | |
if (asprintf(&arg, "%s,%s", arg, str) < 0) { | |
tst_resm(TBROK, "Could not produce self_exec string"); | |
return -1; | |
} | |
break; | |
default: | |
tst_resm(TBROK, "Format string option %c not implemented", *p); | |
return -1; | |
break; | |
} | |
} | |
va_end(ap); | |
if (chdir(start_cwd) < 0) { | |
tst_resm(TBROK, "Could not change to %s for self_exec", start_cwd); | |
return -1; | |
} | |
return execlp(argv0, argv0, "-C", arg, (char *) NULL); | |
} | |
#endif /* UCLINUX */ |