Home | History | Annotate | Download | only in lib
      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 
     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 
     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 
    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