• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2008 FUJITSU LIMITED
4  * Copyright (c) 2021 Joerg Vehlow <joerg.vehlow@aox-tech.de>
5  *
6  * Author: Li Zefan <lizf@cn.fujitsu.com>
7  *
8  * Generate a specified process event (fork, exec, uid, gid or exit).
9  */
10 
11 #include <unistd.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <pwd.h>
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 
19 #define TST_NO_DEFAULT_MAIN
20 #include "tst_test.h"
21 
22 extern struct tst_test *tst_test;
23 static struct tst_test test = {
24 	.forks_child = 1
25 };
26 
27 #define DEFAULT_EVENT_NUM       1
28 
29 unsigned long nr_event = DEFAULT_EVENT_NUM;
30 
31 uid_t ltp_uid;
32 gid_t ltp_gid;
33 const char *ltp_user = "nobody";
34 
35 char **exec_argv;
36 
37 void (*gen_event) (void);
38 static void usage(int status) LTP_ATTRIBUTE_NORETURN;
39 
40 /*
41  * Show the usage
42  *
43  * @param status the exit status
44  */
usage(int status)45 static void usage(int status)
46 {
47 	FILE *stream = (status ? stderr : stdout);
48 
49 	fprintf(stream,
50 		"Usage: event_generator -e fork|exit|exec|uid|gid [-n nr_event]\n");
51 
52 	exit(status);
53 }
54 
55 /*
56  * Generate exec event.
57  *
58  * We can't just exec nr_event times, because the current process image
59  * will be replaced with the new process image, so we use environment
60  * variable as event counters, as it will be inherited after exec.
61  */
gen_exec(void)62 static void gen_exec(void)
63 {
64 	char *val;
65 	char buf[10];
66 	unsigned long nr_exec;
67 
68 	/* get the event counter */
69 	val = getenv("NR_EXEC");
70 	if (!val) {
71 		nr_exec = 0;
72 		setenv("NR_EXEC", "1", 1);
73 	} else {
74 		nr_exec = atoi(val);
75 		snprintf(buf, 10, "%lu", nr_exec + 1);
76 		setenv("NR_EXEC", buf, 1);
77 	}
78 
79 	/* stop generate exec event */
80 	if (nr_exec >= nr_event)
81 		return;
82 
83 	/* fflush is needed before exec */
84 	printf("exec pid: %d\n", getpid());
85 	fflush(stdout);
86 
87 	/* Note: This expects the full path to self in exec_argv[0]! */
88 	SAFE_EXECVP(exec_argv[0], exec_argv);
89 }
90 
91 /*
92  * Generate fork event.
93  */
gen_fork(void)94 static inline void gen_fork(void)
95 {
96 	/* The actual fork is already done in main */
97 	printf("fork parent: %d, child: %d\n", getppid(), getpid());
98 }
99 
100 /**
101  * Generate exit event
102  */
gen_exit(void)103 static inline void gen_exit(void)
104 {
105 	/* exit_signal will always be SIGCHLD, if the process terminates cleanly */
106 	printf("exit pid: %d exit_code: %d exit_signal: %d\n",
107 	       getpid(), 0, SIGCHLD);
108 	/* exit is called by main already */
109 }
110 
111 /*
112  * Generate uid event.
113  */
gen_uid(void)114 static inline void gen_uid(void)
115 {
116 	SAFE_SETUID(ltp_uid);
117 	printf("uid pid: %d euid: %d ruid: %d\n", getpid(), ltp_uid, ltp_uid);
118 }
119 
120 /*
121  * Generate gid event.
122  */
gen_gid(void)123 static inline void gen_gid(void)
124 {
125 	SAFE_SETGID(ltp_gid);
126 	printf("gid pid: %d egid: %d rgid: %u\n", getpid(), ltp_gid, ltp_gid);
127 }
128 
129 /*
130  * Read option from user input.
131  *
132  * @param argc number of arguments
133  * @param argv argument list
134  */
process_options(int argc,char ** argv)135 static void process_options(int argc, char **argv)
136 {
137 	int c;
138 	char *end;
139 
140 	while ((c = getopt(argc, argv, "e:n:h")) != -1) {
141 		switch (c) {
142 			/* which event to generate */
143 		case 'e':
144 			if (!strcmp(optarg, "exec"))
145 				gen_event = gen_exec;
146 			else if (!strcmp(optarg, "fork"))
147 				gen_event = gen_fork;
148 			else if (!strcmp(optarg, "exit"))
149 				gen_event = gen_exit;
150 			else if (!strcmp(optarg, "uid"))
151 				gen_event = gen_uid;
152 			else if (!strcmp(optarg, "gid"))
153 				gen_event = gen_gid;
154 			else {
155 				fprintf(stderr, "wrong -e argument!");
156 				exit(1);
157 			}
158 			break;
159 			/* number of event to generate */
160 		case 'n':
161 			nr_event = strtoul(optarg, &end, 10);
162 			if (*end != '\0' || nr_event == 0) {
163 				fprintf(stderr, "wrong -n argument!");
164 				exit(1);
165 			}
166 			break;
167 			/* help */
168 		case 'h':
169 			usage(0);
170 		default:
171 			fprintf(stderr, "unknown option!\n");
172 			usage(1);
173 		}
174 	}
175 
176 	if (!gen_event) {
177 		fprintf(stderr, "no event type specified!\n");
178 		usage(1);
179 	}
180 }
181 
main(int argc,char ** argv)182 int main(int argc, char **argv)
183 {
184 	unsigned long i;
185 	struct passwd *ent;
186 
187 	tst_test = &test;
188 
189 	process_options(argc, argv);
190 
191 	ent = getpwnam(ltp_user);
192 	if (ent == NULL) {
193 		fprintf(stderr, "can't get password entry for %s", ltp_user);
194 		exit(1);
195 	}
196 	ltp_uid = ent->pw_uid;
197 	ltp_gid = ent->pw_gid;
198 
199 	/* special processing for gen_exec, see comments above gen_exec() */
200 	if (gen_event == gen_exec) {
201 		exec_argv = argv;
202 
203 		gen_exec();
204 
205 		/* won't reach here */
206 		return 0;
207 	}
208 
209 	/* other events */
210 	for (i = 0; i < nr_event; i++) {
211 		pid_t pid;
212 		int status;
213 
214 		pid = SAFE_FORK();
215 		if (pid == 0) {
216 			gen_event();
217 			exit(0);
218 		} else {
219 			if (pid != SAFE_WAITPID(pid, &status, 0)) {
220 				fprintf(stderr,
221 				        "Child process did not terminate as expected\n");
222 				return 1;
223 			}
224 			if (WEXITSTATUS(status) != 0) {
225 				fprintf(stderr, "Child process did not terminate with 0\n");
226 				return 1;
227 			}
228 		}
229 	}
230 
231 	return 0;
232 }
233