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