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