• 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 static uid_t ltp_uid;
28 static gid_t ltp_gid;
29 static const char *ltp_user = "nobody";
30 static char *prog_name;
31 
32 static int checkpoint_id = -1;
33 static int nr_event = 1;
34 
35 static void (*gen_event)(void);
36 
37 static void usage(int status) LTP_ATTRIBUTE_NORETURN;
38 
39 /*
40  * Show the usage
41  *
42  * @param status the exit status
43  */
usage(int status)44 static void usage(int status)
45 {
46 	FILE *stream = (status ? stderr : stdout);
47 
48 	fprintf(stream,
49 		"Usage: event_generator -e fork|exit|exec|uid|gid [-n nr_event] [-c checkpoint_id]\n");
50 
51 	exit(status);
52 }
53 
54 /*
55  * Generate exec event.
56  */
gen_exec(void)57 static void gen_exec(void)
58 {
59 	char buf[10];
60 
61 	/* fflush is needed before exec */
62 	printf("exec pid: %d\n", getpid());
63 	fflush(stdout);
64 
65 	/*
66 	 * Decrease number of events to generate.
67 	 * Don't pass checkpoint_id here. It is only used for synchronizing with
68 	 * the shell script, before the first exec.
69 	 */
70 	sprintf(buf, "%u", nr_event - 1);
71 	SAFE_EXECLP(prog_name, prog_name, "-e", "exec", "-n", buf, NULL);
72 }
73 
74 /*
75  * Generate fork event.
76  */
gen_fork(void)77 static inline void gen_fork(void)
78 {
79 	/* The actual fork is already done in main */
80 	printf("fork parent: %d, child: %d\n", getppid(), getpid());
81 }
82 
83 /**
84  * Generate exit event
85  */
gen_exit(void)86 static inline void gen_exit(void)
87 {
88 	/* exit_signal will always be SIGCHLD, if the process terminates cleanly */
89 	printf("exit pid: %d exit_code: %d exit_signal: %d\n",
90 	       getpid(), 0, SIGCHLD);
91 	/* exit is called by main already */
92 }
93 
94 /*
95  * Generate uid event.
96  */
gen_uid(void)97 static inline void gen_uid(void)
98 {
99 	SAFE_SETUID(ltp_uid);
100 	printf("uid pid: %d euid: %d ruid: %d\n", getpid(), ltp_uid, ltp_uid);
101 }
102 
103 /*
104  * Generate gid event.
105  */
gen_gid(void)106 static inline void gen_gid(void)
107 {
108 	SAFE_SETGID(ltp_gid);
109 	printf("gid pid: %d egid: %d rgid: %u\n", getpid(), ltp_gid, ltp_gid);
110 }
111 
112 /*
113  * Read option from user input.
114  *
115  * @param argc number of arguments
116  * @param argv argument list
117  */
process_options(int argc,char ** argv)118 static void process_options(int argc, char **argv)
119 {
120 	int c;
121 
122 	while ((c = getopt(argc, argv, "e:n:c:h")) != -1) {
123 		switch (c) {
124 			/* which event to generate */
125 		case 'e':
126 			if (!strcmp(optarg, "exec"))
127 				gen_event = gen_exec;
128 			else if (!strcmp(optarg, "fork"))
129 				gen_event = gen_fork;
130 			else if (!strcmp(optarg, "exit"))
131 				gen_event = gen_exit;
132 			else if (!strcmp(optarg, "uid"))
133 				gen_event = gen_uid;
134 			else if (!strcmp(optarg, "gid"))
135 				gen_event = gen_gid;
136 			else {
137 				fprintf(stderr, "wrong -e argument!");
138 				exit(1);
139 			}
140 			break;
141 			/* number of event to generate */
142 		case 'n':
143 			if (tst_parse_int(optarg, &nr_event, 0, INT_MAX)) {
144 				fprintf(stderr, "invalid value for nr_event");
145 				usage(1);
146 			}
147 			break;
148 		case 'c':
149 			if (tst_parse_int(optarg, &checkpoint_id, 0, INT_MAX)) {
150 				fprintf(stderr, "invalid value for checkpoint_id");
151 				usage(1);
152 			}
153 			break;
154 			/* help */
155 		case 'h':
156 			usage(0);
157 		default:
158 			usage(1);
159 		}
160 	}
161 
162 	if (!gen_event) {
163 		fprintf(stderr, "no event type specified!\n");
164 		usage(1);
165 	}
166 }
167 
main(int argc,char ** argv)168 int main(int argc, char **argv)
169 {
170 	int i;
171 	struct passwd *ent;
172 
173 	prog_name = argv[0];
174 
175 	tst_test = &test;
176 
177 	process_options(argc, argv);
178 
179 	ent = getpwnam(ltp_user);
180 	if (ent == NULL) {
181 		fprintf(stderr, "can't get password entry for %s", ltp_user);
182 		exit(1);
183 	}
184 	ltp_uid = ent->pw_uid;
185 	ltp_gid = ent->pw_gid;
186 
187 	/* ready to generate events */
188 	if (checkpoint_id != -1) {
189 		tst_reinit();
190 		TST_CHECKPOINT_WAIT(checkpoint_id);
191 	}
192 
193 	if (gen_event == gen_exec) {
194 		/*
195 		 * The nr_event events are generated,
196 		 * by recursively replacing ourself with
197 		 * a fresh copy, decrementing the number of events
198 		 * for each execution
199 		 */
200 		if (nr_event != 0)
201 			gen_exec();
202 		return 0;
203 	}
204 
205 	/* other events */
206 	for (i = 0; i < nr_event; i++) {
207 		pid_t pid;
208 		int status;
209 
210 		pid = SAFE_FORK();
211 		if (pid == 0) {
212 			gen_event();
213 			exit(0);
214 		} else {
215 			if (pid != SAFE_WAITPID(pid, &status, 0)) {
216 				fprintf(stderr,
217 				        "Child process did not terminate as expected\n");
218 				return 1;
219 			}
220 			if (WEXITSTATUS(status) != 0) {
221 				fprintf(stderr, "Child process did not terminate with 0\n");
222 				return 1;
223 			}
224 			/*
225 			 * We need a tiny sleep here, so the kernel can generate
226 			 * exit events in the correct order.
227 			 * Otherwise it can happen, that exit events are generated
228 			 * out-of-order.
229 			 */
230 			if (gen_event == gen_exit)
231 				usleep(100);
232 		}
233 	}
234 
235 	return 0;
236 }
237