| /* -*- 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 */ |