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 
     36 /* Set from parse_opts.c: */
     37 char *child_args;		/* Arguments to child when -C is used */
     38 
     39 static char *start_cwd;		/* Stores the starting directory for self_exec */
     40 
     41 int 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 
     70 void 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 
    167 int 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