File: CommandTest.c

package info (click to toggle)
monit 1%3A5.35.2-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 8,228 kB
  • sloc: ansic: 31,202; yacc: 4,634; sh: 4,071; lex: 1,190; pascal: 480; makefile: 285
file content (430 lines) | stat: -rw-r--r-- 16,675 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
#include "Config.h"

#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <signal.h>
#include <stdarg.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>

#include "Bootstrap.h"
#include "Str.h"
#include "List.h"
#include "File.h"
#include "system/System.h"
#include "system/Command.h"
#include "system/Time.h"

/**
 * Command.c unit tests.
 */


static void onExec(Process_T P) {
        assert(P);
        char buf[STRLEN];
        // Child process info
        printf("\tSubprocess ((pid=%d)\n", Process_pid(P));
        InputStream_T in = Process_inputStream(P);
        OutputStream_T out = Process_outputStream(P);
        InputStream_T err = Process_errorStream(P);
        printf("\tSub-Process is %s\n", Process_isRunning(P) ? "running" : "not running");
        printf("\tCommunication with child:\n");
        if (! InputStream_readLine(in, buf, STRLEN)) {
                InputStream_readLine(err, buf, STRLEN);
                printf("\tError in script: %s\n", Str_chomp(buf));
        } else {
                printf("\t%s", buf);
                OutputStream_print(out, "Elessar Telcontar\n");
                assert(OutputStream_flush(out) > 0);
                char *line = InputStream_readLine(in, buf, STRLEN);
                assert(line);
                printf("\t%s", line);
        }
        printf("\tProcess exited with status: %d\n", Process_waitFor(P));
        Process_free(&P);
}


static void onTerminate(Process_T P) {
        assert(P);
        printf("\tTest terminate subprocess ((pid=%d)\n", Process_pid(P));
        assert(Process_isRunning(P));
#ifdef NETBSD
        // NetBSD: If we call the Process_terminate() immediately, the child process sometimes misses the signal (despite kill() in Process_terminate returned no error) and
        //         exits normally, after the child finished execution. When we wait a bit before sending signal to the child, everything works properly. It seems that the child
        //         is maybe not ready to run yet when we send the signal and NetBSD somehow loses it. Observed only on *NetBSD*
        Time_usleep(500000LL); // Sleep for 500 ms (500,000 µs)
#endif
        assert(Process_terminate(P));
        printf("\tProcess exited with status: %d\n", Process_waitFor(P));
        assert(Process_exitStatus(P) == SIGTERM);
        Process_free(&P);
}


static void onKill(Process_T P) {
        assert(P);
        printf("\tTest kill subprocess ((pid=%d)\n", Process_pid(P));
        assert(Process_isRunning(P));
#ifdef NETBSD
        // NetBSD: If we call the Process_kill() immediately, the child process sometimes misses the signal (despite kill() in Process_kill returned no error) and
        //         exits normally, after the child finished execution. When we wait a bit before sending signal to the child, everything works properly. It seems that the child
        //         is maybe not ready to run yet when we send the signal and NetBSD somehow loses it. Observed only on *NetBSD*
        Time_usleep(500000LL); // Sleep for 500 ms (500,000 µs)
#endif
        assert(Process_kill(P));
        printf("\tProcess exited with status: %d\n", Process_waitFor(P));
        assert(Process_exitStatus(P) == SIGKILL);
        Process_free(&P);
}


static void onEnv(Process_T P) {
        assert(P);
        char buf[STRLEN];
        InputStream_T in = Process_inputStream(P);
        assert(InputStream_readLine(in, buf, STRLEN));
        assert(Str_isEqual(Str_chomp(buf), "Ylajali"));
        // Assert that sub-process environment is not set in main process
        assert(! getenv("SULT"));
        printf("\tEnvironment Variable in sub-process only: $SULT = %s\n", buf);
        Process_free(&P);
        assert(! P);
}

static void onDetach(Process_T P) {
        assert(P);
        File_delete("/tmp/ondetach");
        // Assert the process is running, blocking on read
        assert(Process_isRunning(P));
        // Close pipes and streams, this will cause read in the script to return with eof
        Process_detach(P);
        assert(Process_isdetached(P));
        // Streams should be closed and not available after a detach
        assert(Process_inputStream(P) == NULL);
        // Assert that the script exited cleanly
        assert(Process_waitFor(P) == 0);
        Process_free(&P);
        // Finally assert that the script did continue and wrote this file
        assert(File_delete("/tmp/ondetach"));
}

static void onChild(Process_T P) {
        assert(P);
        printf("\tStarted process (pid=%d)\n", Process_pid(P));
        // Verify process is running
        assert(Process_isRunning(P));
        printf("\tProcess is running\n");
        // Wait longer than sleep duration for SIGCHLD delivery
        printf("\tWaiting for SIGCHLD delivery...\n");
        Time_sleep(2); // 2 seconds
        // Status should have been set by SIGCHLD handler
        assert(!Process_isRunning(P));
        printf("\tProcess is no longer running\n");
        // Check exit status is set by SIGCHLD handler
        int status = Process_exitStatus(P);
        printf("\tProcess exit status: %d\n", status);
        assert(status == 0);
        Process_free(&P);
        assert(! P);
}


int main(void) {

        Bootstrap(); // Need to initialize library

        printf("============> Start Command Tests\n\n");


        printf("=> Test1: create/destroy\n");
        {
                Command_T c = Command_new("/bin/sh", "-c", "ps -aef|grep monit");
                assert(c);
                Command_free(&c);
                assert(!c);
        }
        printf("=> Test1: OK\n\n");

        printf("=> Test2: set and get uid/gid\n");
        {
                Command_T c = Command_new("/bin/sh", "-c", "ps -aef|grep monit");
                // Check that default is 0
                assert(Command_uid(c) == 0);
                assert(Command_gid(c) == 0);
                if (getuid() == 0) {
                        Command_setUid(c,42);
                        assert(Command_uid(c) == 42);
                        Command_setGid(c,148);
                        assert(Command_gid(c) == 148);
                        Command_free(&c);
                } else {
                        TRY
                        {
                                printf("\tNot running as root. Checking exception instead: ");
                                Command_setUid(c,42);
                                printf("AssertException not thrown\n");
                                exit(1);
                        }
                        CATCH (AssertException)
                        {
                                printf("ok\n");
                        }
                        FINALLY
                        {
                                Command_free(&c);
                        }
                        END_TRY;
                }
        }
        printf("=> Test2: OK\n\n");

        printf("=> Test4: set and get env\n");
        {
                Command_T c = Command_new("/bin/sh", "-c", "ps -aef|grep monit");
                // Set and get env string
                Command_setEnv(c, "PATH", "/usr/bin");
                Command_setEnv(c, "SHELL", "/bin/bash");
                Command_setEnv(c, "PAT", "Carroll");
                assert(Str_isEqual(Command_env(c, "PATH"), "/usr/bin"));
                assert(Str_isEqual(Command_env(c, "SHELL"), "/bin/bash"));
                assert(Str_isEqual(Command_env(c, "PAT"), "Carroll"));
                // Empty and NULL value
                Command_setEnv(c, "PATH", "");
                Command_setEnv(c, "SHELL", NULL);
                assert(Str_isEqual(Command_env(c, "PATH"), ""));
                assert(Str_isEqual(Command_env(c, "SHELL"), ""));
                // Unknown variable should result in NULL
                assert(Command_env(c, "UKNOWNVARIABLE") == NULL);
                // vSetEnv
                Command_vSetEnv(c, "PID", "%ld", (long)getpid());
                assert(Str_parseLLong(Command_env(c, "PID")) > 1);
                Command_vSetEnv(c, "ZERO", NULL);
                assert(Str_isEqual(Command_env(c, "ZERO"), ""));
                Command_free(&c);
        }
        printf("=> Test4: OK\n\n");

        printf("=> Test5: set and get Command\n");
        {
                Command_T c = Command_new("/bin/sh", "-c", "ps -aef|grep monit");
                List_T l = Command_command(c);
                assert(Str_isEqual(l->head->e, "/bin/sh"));
                assert(Str_isEqual(l->head->next->e, "-c"));
                assert(Str_isEqual(l->head->next->next->e, "ps -aef|grep monit"));
                Command_free(&c);
        }
        printf("=> Test5: OK\n\n");

        printf("=> Test6: Append arguments\n");
        {
                Command_T c = Command_new("/bin/ls");
                Command_appendArgument(c, "-l");
                Command_appendArgument(c, "-t");
                Command_appendArgument(c, "-r");
                List_T l = Command_command(c);
                assert(Str_isEqual(l->head->e, "/bin/ls"));
                assert(Str_isEqual(l->head->next->e, "-l"));
                assert(Str_isEqual(l->head->next->next->e, "-t"));
                assert(Str_isEqual(l->head->next->next->next->e, "-r"));
                assert(l->head->next->next->next->next == NULL);
                Command_free(&c);
        }
        printf("=> Test6: OK\n\n");

        printf("=> Test7: execute invalid program\n");
        {
                // Program producing error
                Command_T c = Command_new("/bin/sh", "-c", "not_a_program;");
                Command_setDir(c, "/");
                printf("\tThis should produce an error:\n");
                onExec(Command_execute(c));
                Command_free(&c);
                // Nonexistent program
                TRY
                {
                        Command_new("/bla/bla/123");
                        exit(1);
                }
                CATCH (AssertException)
                END_TRY;
        }
        printf("=> Test7: OK\n\n");

        printf("=> Test8: execute valid program\n");
        {
                Command_T c = Command_new("/bin/sh", "-c", "echo \"Please enter your name:\";read name;echo \"Hello $name\";");
                onExec(Command_execute(c));
                Command_free(&c);
        }
        printf("=> Test8: OK\n\n");

        printf("=> Test9: terminate sub-process\n");
        {
                Command_T c = Command_new("/bin/sh", "-c", "exec sleep 30;");
                onTerminate(Command_execute(c));
                Command_free(&c);
        }
        printf("=> Test9: OK\n\n");

        printf("=> Test10: kill sub-process\n");
        {
                Command_T c = Command_new("/bin/sh", "-c", "trap 1 2 15; sleep 30; ");
                onKill(Command_execute(c));
                Command_free(&c);
        }
        printf("=> Test10: OK\n\n");

        printf("=> Test11: environment in sub-process\n");
        {
                Command_T c = Command_new("/bin/sh", "-c", "echo $SULT");
                // Set environment in sub-process only
                Command_setEnv(c, "SULT", "Ylajali");
                onEnv(Command_execute(c));
                Command_free(&c);
        }
        printf("=> Test11: OK\n\n");

        printf("=> Test12: on execve(2) error\n");
        {
                // Executing a directory should produce an execve error
                Command_T c = Command_new("/tmp");
                Process_T p = Command_execute(c);
                assert(! p);
                Command_free(&c);
                printf("\tOK, got execve error -- %s\n", System_lastError());
        }
        printf("=> Test12: OK\n\n");

        printf("=> Test13: chdir\n");
        {
                Command_T c = Command_new("/bin/sh", "-c", "echo $$ > chdirtest;");
                Command_setDir(c, "/tmp/");
                Process_T p = Command_execute(c);
                assert(Process_waitFor(p) == 0);
                Process_free(&p);
                assert(File_delete("/tmp/chdirtest"));
                TRY
                {
                        Command_setDir(c, "/tmp/somenonexistingdir");
                        exit(1);
                }
                CATCH (AssertException)
                END_TRY;
                Command_free(&c);
        }
        printf("=> Test13: OK\n\n");

        printf("=> Test14: detach\n");
        {
                Command_T c = Command_new("/bin/sh", "-c", "read msg; echo \"this write will fail but should not exit the script\"; echo \"$$ still alive\" > /tmp/ondetach; exit 0;");
                onDetach(Command_execute(c));
                Command_free(&c);
        }
        printf("=> Test14: OK\n\n");

        printf("=> Test15: setuid and setgid in sub-process\n");
        {
                if (getuid() != 0) {
                        printf("\tCannot run test: not running as root\n");
                        goto skip;
                }

                struct passwd *pwd = getpwnam("daemon");
                assert(pwd && "daemon user not found");

                char *script = Str_cat("if test $(id -u) -eq %d -a $(id -g) -eq %d; then exit 0; fi; exit 1;",
                                     pwd->pw_uid, pwd->pw_gid);
                Command_T c = Command_new("/bin/sh", "-c", script);
                Command_setDir(c, "/tmp");
                Command_setUid(c, pwd->pw_uid);
                Command_setGid(c, pwd->pw_gid);
                Process_T p = Command_execute(c);
                assert(p);
                assert(Process_waitFor(p) == 0);
                Process_free(&p);
                Command_free(&c);
                FREE(script);
        }
        skip:
        printf("=> Test15: OK\n\n");

        printf("=> Test16: set umask\n");
        {
                char *script = "tmp=\"/tmp/$$.tst\";touch $tmp;permissions="
#if defined(LINUX) || defined(SOLARIS)
                "$(stat -c '%a' $tmp);"
#elif DARWIN
                "$(stat -f '%A' $tmp);"
#else  /* BSD systems */
                "$(stat -f '%Lp' $tmp);"
#endif
                "rm -f $tmp; if test $permissions -eq 642; then exit 0; fi; exit 1;";
                Command_T c = Command_new("/bin/sh", "-c", script);
                Command_setUmask(c, 025);
                Process_T p = Command_execute(c);
                assert(p);
                assert(Process_waitFor(p) == 0);
                Process_free(&p);
                Command_free(&c);
        }
        printf("=> Test16: OK\n\n");

        printf("=> Test17: SIGCHLD handling\n");
        {
                Command_T c = Command_new("/bin/sh", "-c", "sleep 1; exit 0");
                onChild(Command_execute(c));
                Command_free(&c);
        }
        printf("=> Test17: OK\n\n");

        printf("=> Test18: sequential process handling\n");
        {
                // Start both processes first
                Command_T c1 = Command_new("/bin/sh", "-c", "exec 1>&2; echo hello from process1");
                Command_T c2 = Command_new("/bin/sh", "-c", "echo hello from process2");
                Process_T p1 = Command_execute(c1);
                Process_T p2 = Command_execute(c2);
                assert(p1 && p2);
                
                char buf1[STRLEN] = {};
                char buf2[STRLEN] = {};
                
                // Handle first process completely
                InputStream_T err1 = Process_errorStream(p1);
                // Assert output went to stderr
                assert(InputStream_readBytes(err1, buf1, sizeof(buf1) - 1) > 0);

                // Free and recreate p1 before handling p2
                Process_free(&p1);
                assert(p1 == NULL);
                p1 = Command_execute(c1);
                assert(p1);

                // Now handle second process
                InputStream_T err2 = Process_errorStream(p2);
                if (!InputStream_readLine(err2, buf2, sizeof(buf2) - 1)) {
                        InputStream_T in2 = Process_inputStream(p2);
                        InputStream_readLine(in2, buf2, sizeof(buf2) - 1);
                }

                // Verify content
                assert(Str_startsWith(buf1, "hello from process1"));
                assert(Str_startsWith(buf2, "hello from process2"));
                
                // Cleanup
                Process_free(&p1);
                Process_free(&p2);
                Command_free(&c1);
                Command_free(&c2);
        }
        printf("=> Test18: OK\n\n");
        
        printf("============> Command Tests: OK\n\n");

}