]>
Commit | Line | Data |
---|---|---|
d9c6a72d LR |
1 | #!/bin/bash |
2 | # | |
3 | # Copyright (C) 2017 Luis R. Rodriguez <mcgrof@kernel.org> | |
4 | # | |
5 | # This program is free software; you can redistribute it and/or modify it | |
6 | # under the terms of the GNU General Public License as published by the Free | |
7 | # Software Foundation; either version 2 of the License, or at your option any | |
8 | # later version; or, when distributed separately from the Linux kernel or | |
9 | # when incorporated into other software packages, subject to the following | |
10 | # license: | |
11 | # | |
12 | # This program is free software; you can redistribute it and/or modify it | |
13 | # under the terms of copyleft-next (version 0.3.1 or later) as published | |
14 | # at http://copyleft-next.org/. | |
15 | ||
16 | # This is a stress test script for kmod, the kernel module loader. It uses | |
17 | # test_kmod which exposes a series of knobs for the API for us so we can | |
18 | # tweak each test in userspace rather than in kernelspace. | |
19 | # | |
20 | # The way kmod works is it uses the kernel's usermode helper API to eventually | |
21 | # call /sbin/modprobe. It has a limit of the number of concurrent calls | |
22 | # possible. The kernel interface to load modules is request_module(), however | |
23 | # mount uses get_fs_type(). Both behave slightly differently, but the | |
24 | # differences are important enough to test each call separately. For this | |
25 | # reason test_kmod starts by providing tests for both calls. | |
26 | # | |
27 | # The test driver test_kmod assumes a series of defaults which you can | |
28 | # override by exporting to your environment prior running this script. | |
29 | # For instance this script assumes you do not have xfs loaded upon boot. | |
30 | # If this is false, export DEFAULT_KMOD_FS="ext4" prior to running this | |
31 | # script if the filesyste module you don't have loaded upon bootup | |
32 | # is ext4 instead. Refer to allow_user_defaults() for a list of user | |
33 | # override variables possible. | |
34 | # | |
35 | # You'll want at least 4 GiB of RAM to expect to run these tests | |
36 | # without running out of memory on them. For other requirements refer | |
37 | # to test_reqs() | |
38 | ||
39 | set -e | |
40 | ||
41 | TEST_NAME="kmod" | |
42 | TEST_DRIVER="test_${TEST_NAME}" | |
43 | TEST_DIR=$(dirname $0) | |
44 | ||
45 | # This represents | |
46 | # | |
47 | # TEST_ID:TEST_COUNT:ENABLED | |
48 | # | |
49 | # TEST_ID: is the test id number | |
50 | # TEST_COUNT: number of times we should run the test | |
51 | # ENABLED: 1 if enabled, 0 otherwise | |
52 | # | |
53 | # Once these are enabled please leave them as-is. Write your own test, | |
54 | # we have tons of space. | |
55 | ALL_TESTS="0001:3:1" | |
56 | ALL_TESTS="$ALL_TESTS 0002:3:1" | |
57 | ALL_TESTS="$ALL_TESTS 0003:1:1" | |
58 | ALL_TESTS="$ALL_TESTS 0004:1:1" | |
59 | ALL_TESTS="$ALL_TESTS 0005:10:1" | |
60 | ALL_TESTS="$ALL_TESTS 0006:10:1" | |
61 | ALL_TESTS="$ALL_TESTS 0007:5:1" | |
6d7964a7 LR |
62 | ALL_TESTS="$ALL_TESTS 0008:150:1" |
63 | ALL_TESTS="$ALL_TESTS 0009:150:1" | |
d9c6a72d LR |
64 | |
65 | test_modprobe() | |
66 | { | |
67 | if [ ! -d $DIR ]; then | |
68 | echo "$0: $DIR not present" >&2 | |
69 | echo "You must have the following enabled in your kernel:" >&2 | |
70 | cat $TEST_DIR/config >&2 | |
71 | exit 1 | |
72 | fi | |
73 | } | |
74 | ||
75 | function allow_user_defaults() | |
76 | { | |
77 | if [ -z $DEFAULT_KMOD_DRIVER ]; then | |
78 | DEFAULT_KMOD_DRIVER="test_module" | |
79 | fi | |
80 | ||
81 | if [ -z $DEFAULT_KMOD_FS ]; then | |
82 | DEFAULT_KMOD_FS="xfs" | |
83 | fi | |
84 | ||
85 | if [ -z $PROC_DIR ]; then | |
86 | PROC_DIR="/proc/sys/kernel/" | |
87 | fi | |
88 | ||
89 | if [ -z $MODPROBE_LIMIT ]; then | |
90 | MODPROBE_LIMIT=50 | |
91 | fi | |
92 | ||
93 | if [ -z $DIR ]; then | |
94 | DIR="/sys/devices/virtual/misc/${TEST_DRIVER}0/" | |
95 | fi | |
96 | ||
97 | if [ -z $DEFAULT_NUM_TESTS ]; then | |
98 | DEFAULT_NUM_TESTS=150 | |
99 | fi | |
100 | ||
101 | MODPROBE_LIMIT_FILE="${PROC_DIR}/kmod-limit" | |
102 | } | |
103 | ||
104 | test_reqs() | |
105 | { | |
106 | if ! which modprobe 2> /dev/null > /dev/null; then | |
107 | echo "$0: You need modprobe installed" >&2 | |
108 | exit 1 | |
109 | fi | |
110 | ||
111 | if ! which kmod 2> /dev/null > /dev/null; then | |
112 | echo "$0: You need kmod installed" >&2 | |
113 | exit 1 | |
114 | fi | |
115 | ||
116 | # kmod 19 has a bad bug where it returns 0 when modprobe | |
117 | # gets called *even* if the module was not loaded due to | |
118 | # some bad heuristics. For details see: | |
119 | # | |
120 | # A work around is possible in-kernel but its rather | |
121 | # complex. | |
122 | KMOD_VERSION=$(kmod --version | awk '{print $3}') | |
123 | if [[ $KMOD_VERSION -le 19 ]]; then | |
124 | echo "$0: You need at least kmod 20" >&2 | |
125 | echo "kmod <= 19 is buggy, for details see:" >&2 | |
126 | echo "http://git.kernel.org/cgit/utils/kernel/kmod/kmod.git/commit/libkmod/libkmod-module.c?id=fd44a98ae2eb5eb32161088954ab21e58e19dfc4" >&2 | |
127 | exit 1 | |
128 | fi | |
129 | ||
130 | uid=$(id -u) | |
131 | if [ $uid -ne 0 ]; then | |
132 | echo $msg must be run as root >&2 | |
133 | exit 0 | |
134 | fi | |
135 | } | |
136 | ||
137 | function load_req_mod() | |
138 | { | |
139 | trap "test_modprobe" EXIT | |
140 | ||
141 | if [ ! -d $DIR ]; then | |
142 | # Alanis: "Oh isn't it ironic?" | |
143 | modprobe $TEST_DRIVER | |
144 | fi | |
145 | } | |
146 | ||
147 | test_finish() | |
148 | { | |
149 | echo "Test completed" | |
150 | } | |
151 | ||
152 | errno_name_to_val() | |
153 | { | |
154 | case "$1" in | |
155 | # kmod calls modprobe and upon of a module not found | |
156 | # modprobe returns just 1... However in the kernel we | |
157 | # *sometimes* see 256... | |
158 | MODULE_NOT_FOUND) | |
159 | echo 256;; | |
160 | SUCCESS) | |
161 | echo 0;; | |
162 | -EPERM) | |
163 | echo -1;; | |
164 | -ENOENT) | |
165 | echo -2;; | |
166 | -EINVAL) | |
167 | echo -22;; | |
168 | -ERR_ANY) | |
169 | echo -123456;; | |
170 | *) | |
171 | echo invalid;; | |
172 | esac | |
173 | } | |
174 | ||
175 | errno_val_to_name() | |
176 | case "$1" in | |
177 | 256) | |
178 | echo MODULE_NOT_FOUND;; | |
179 | 0) | |
180 | echo SUCCESS;; | |
181 | -1) | |
182 | echo -EPERM;; | |
183 | -2) | |
184 | echo -ENOENT;; | |
185 | -22) | |
186 | echo -EINVAL;; | |
187 | -123456) | |
188 | echo -ERR_ANY;; | |
189 | *) | |
190 | echo invalid;; | |
191 | esac | |
192 | ||
193 | config_set_test_case_driver() | |
194 | { | |
195 | if ! echo -n 1 >$DIR/config_test_case; then | |
196 | echo "$0: Unable to set to test case to driver" >&2 | |
197 | exit 1 | |
198 | fi | |
199 | } | |
200 | ||
201 | config_set_test_case_fs() | |
202 | { | |
203 | if ! echo -n 2 >$DIR/config_test_case; then | |
204 | echo "$0: Unable to set to test case to fs" >&2 | |
205 | exit 1 | |
206 | fi | |
207 | } | |
208 | ||
209 | config_num_threads() | |
210 | { | |
211 | if ! echo -n $1 >$DIR/config_num_threads; then | |
212 | echo "$0: Unable to set to number of threads" >&2 | |
213 | exit 1 | |
214 | fi | |
215 | } | |
216 | ||
217 | config_get_modprobe_limit() | |
218 | { | |
219 | if [[ -f ${MODPROBE_LIMIT_FILE} ]] ; then | |
220 | MODPROBE_LIMIT=$(cat $MODPROBE_LIMIT_FILE) | |
221 | fi | |
222 | echo $MODPROBE_LIMIT | |
223 | } | |
224 | ||
225 | config_num_thread_limit_extra() | |
226 | { | |
227 | MODPROBE_LIMIT=$(config_get_modprobe_limit) | |
228 | let EXTRA_LIMIT=$MODPROBE_LIMIT+$1 | |
229 | config_num_threads $EXTRA_LIMIT | |
230 | } | |
231 | ||
232 | # For special characters use printf directly, | |
233 | # refer to kmod_test_0001 | |
234 | config_set_driver() | |
235 | { | |
236 | if ! echo -n $1 >$DIR/config_test_driver; then | |
237 | echo "$0: Unable to set driver" >&2 | |
238 | exit 1 | |
239 | fi | |
240 | } | |
241 | ||
242 | config_set_fs() | |
243 | { | |
244 | if ! echo -n $1 >$DIR/config_test_fs; then | |
245 | echo "$0: Unable to set driver" >&2 | |
246 | exit 1 | |
247 | fi | |
248 | } | |
249 | ||
250 | config_get_driver() | |
251 | { | |
252 | cat $DIR/config_test_driver | |
253 | } | |
254 | ||
255 | config_get_test_result() | |
256 | { | |
257 | cat $DIR/test_result | |
258 | } | |
259 | ||
260 | config_reset() | |
261 | { | |
262 | if ! echo -n "1" >"$DIR"/reset; then | |
263 | echo "$0: reset shuld have worked" >&2 | |
264 | exit 1 | |
265 | fi | |
266 | } | |
267 | ||
268 | config_show_config() | |
269 | { | |
270 | echo "----------------------------------------------------" | |
271 | cat "$DIR"/config | |
272 | echo "----------------------------------------------------" | |
273 | } | |
274 | ||
275 | config_trigger() | |
276 | { | |
277 | if ! echo -n "1" >"$DIR"/trigger_config 2>/dev/null; then | |
278 | echo "$1: FAIL - loading should have worked" | |
279 | config_show_config | |
280 | exit 1 | |
281 | fi | |
282 | echo "$1: OK! - loading kmod test" | |
283 | } | |
284 | ||
285 | config_trigger_want_fail() | |
286 | { | |
287 | if echo "1" > $DIR/trigger_config 2>/dev/null; then | |
288 | echo "$1: FAIL - test case was expected to fail" | |
289 | config_show_config | |
290 | exit 1 | |
291 | fi | |
292 | echo "$1: OK! - kmod test case failed as expected" | |
293 | } | |
294 | ||
295 | config_expect_result() | |
296 | { | |
297 | RC=$(config_get_test_result) | |
298 | RC_NAME=$(errno_val_to_name $RC) | |
299 | ||
300 | ERRNO_NAME=$2 | |
301 | ERRNO=$(errno_name_to_val $ERRNO_NAME) | |
302 | ||
303 | if [[ $ERRNO_NAME = "-ERR_ANY" ]]; then | |
304 | if [[ $RC -ge 0 ]]; then | |
305 | echo "$1: FAIL, test expects $ERRNO_NAME - got $RC_NAME ($RC)" >&2 | |
306 | config_show_config | |
307 | exit 1 | |
308 | fi | |
309 | elif [[ $RC != $ERRNO ]]; then | |
310 | echo "$1: FAIL, test expects $ERRNO_NAME ($ERRNO) - got $RC_NAME ($RC)" >&2 | |
311 | config_show_config | |
312 | exit 1 | |
313 | fi | |
314 | echo "$1: OK! - Return value: $RC ($RC_NAME), expected $ERRNO_NAME" | |
315 | } | |
316 | ||
317 | kmod_defaults_driver() | |
318 | { | |
319 | config_reset | |
320 | modprobe -r $DEFAULT_KMOD_DRIVER | |
321 | config_set_driver $DEFAULT_KMOD_DRIVER | |
322 | } | |
323 | ||
324 | kmod_defaults_fs() | |
325 | { | |
326 | config_reset | |
327 | modprobe -r $DEFAULT_KMOD_FS | |
328 | config_set_fs $DEFAULT_KMOD_FS | |
329 | config_set_test_case_fs | |
330 | } | |
331 | ||
332 | kmod_test_0001_driver() | |
333 | { | |
334 | NAME='\000' | |
335 | ||
336 | kmod_defaults_driver | |
337 | config_num_threads 1 | |
338 | printf '\000' >"$DIR"/config_test_driver | |
339 | config_trigger ${FUNCNAME[0]} | |
340 | config_expect_result ${FUNCNAME[0]} MODULE_NOT_FOUND | |
341 | } | |
342 | ||
343 | kmod_test_0001_fs() | |
344 | { | |
345 | NAME='\000' | |
346 | ||
347 | kmod_defaults_fs | |
348 | config_num_threads 1 | |
349 | printf '\000' >"$DIR"/config_test_fs | |
350 | config_trigger ${FUNCNAME[0]} | |
351 | config_expect_result ${FUNCNAME[0]} -EINVAL | |
352 | } | |
353 | ||
354 | kmod_test_0001() | |
355 | { | |
356 | kmod_test_0001_driver | |
357 | kmod_test_0001_fs | |
358 | } | |
359 | ||
360 | kmod_test_0002_driver() | |
361 | { | |
362 | NAME="nope-$DEFAULT_KMOD_DRIVER" | |
363 | ||
364 | kmod_defaults_driver | |
365 | config_set_driver $NAME | |
366 | config_num_threads 1 | |
367 | config_trigger ${FUNCNAME[0]} | |
368 | config_expect_result ${FUNCNAME[0]} MODULE_NOT_FOUND | |
369 | } | |
370 | ||
371 | kmod_test_0002_fs() | |
372 | { | |
373 | NAME="nope-$DEFAULT_KMOD_FS" | |
374 | ||
375 | kmod_defaults_fs | |
376 | config_set_fs $NAME | |
377 | config_trigger ${FUNCNAME[0]} | |
378 | config_expect_result ${FUNCNAME[0]} -EINVAL | |
379 | } | |
380 | ||
381 | kmod_test_0002() | |
382 | { | |
383 | kmod_test_0002_driver | |
384 | kmod_test_0002_fs | |
385 | } | |
386 | ||
387 | kmod_test_0003() | |
388 | { | |
389 | kmod_defaults_fs | |
390 | config_num_threads 1 | |
391 | config_trigger ${FUNCNAME[0]} | |
392 | config_expect_result ${FUNCNAME[0]} SUCCESS | |
393 | } | |
394 | ||
395 | kmod_test_0004() | |
396 | { | |
397 | kmod_defaults_fs | |
398 | config_num_threads 2 | |
399 | config_trigger ${FUNCNAME[0]} | |
400 | config_expect_result ${FUNCNAME[0]} SUCCESS | |
401 | } | |
402 | ||
403 | kmod_test_0005() | |
404 | { | |
405 | kmod_defaults_driver | |
406 | config_trigger ${FUNCNAME[0]} | |
407 | config_expect_result ${FUNCNAME[0]} SUCCESS | |
408 | } | |
409 | ||
410 | kmod_test_0006() | |
411 | { | |
412 | kmod_defaults_fs | |
413 | config_trigger ${FUNCNAME[0]} | |
414 | config_expect_result ${FUNCNAME[0]} SUCCESS | |
415 | } | |
416 | ||
417 | kmod_test_0007() | |
418 | { | |
419 | kmod_test_0005 | |
420 | kmod_test_0006 | |
421 | } | |
422 | ||
423 | kmod_test_0008() | |
424 | { | |
425 | kmod_defaults_driver | |
426 | MODPROBE_LIMIT=$(config_get_modprobe_limit) | |
427 | let EXTRA=$MODPROBE_LIMIT/6 | |
428 | config_num_thread_limit_extra $EXTRA | |
429 | config_trigger ${FUNCNAME[0]} | |
430 | config_expect_result ${FUNCNAME[0]} SUCCESS | |
431 | } | |
432 | ||
433 | kmod_test_0009() | |
434 | { | |
435 | kmod_defaults_fs | |
436 | MODPROBE_LIMIT=$(config_get_modprobe_limit) | |
437 | let EXTRA=$MODPROBE_LIMIT/4 | |
438 | config_num_thread_limit_extra $EXTRA | |
439 | config_trigger ${FUNCNAME[0]} | |
440 | config_expect_result ${FUNCNAME[0]} SUCCESS | |
441 | } | |
442 | ||
443 | list_tests() | |
444 | { | |
445 | echo "Test ID list:" | |
446 | echo | |
447 | echo "TEST_ID x NUM_TEST" | |
448 | echo "TEST_ID: Test ID" | |
449 | echo "NUM_TESTS: Number of recommended times to run the test" | |
450 | echo | |
451 | echo "0001 x $(get_test_count 0001) - Simple test - 1 thread for empty string" | |
452 | echo "0002 x $(get_test_count 0002) - Simple test - 1 thread for modules/filesystems that do not exist" | |
453 | echo "0003 x $(get_test_count 0003) - Simple test - 1 thread for get_fs_type() only" | |
454 | echo "0004 x $(get_test_count 0004) - Simple test - 2 threads for get_fs_type() only" | |
455 | echo "0005 x $(get_test_count 0005) - multithreaded tests with default setup - request_module() only" | |
456 | echo "0006 x $(get_test_count 0006) - multithreaded tests with default setup - get_fs_type() only" | |
457 | echo "0007 x $(get_test_count 0007) - multithreaded tests with default setup test request_module() and get_fs_type()" | |
458 | echo "0008 x $(get_test_count 0008) - multithreaded - push kmod_concurrent over max_modprobes for request_module()" | |
459 | echo "0009 x $(get_test_count 0009) - multithreaded - push kmod_concurrent over max_modprobes for get_fs_type()" | |
460 | } | |
461 | ||
462 | usage() | |
463 | { | |
464 | NUM_TESTS=$(grep -o ' ' <<<"$ALL_TESTS" | grep -c .) | |
465 | let NUM_TESTS=$NUM_TESTS+1 | |
466 | MAX_TEST=$(printf "%04d\n" $NUM_TESTS) | |
467 | echo "Usage: $0 [ -t <4-number-digit> ] | [ -w <4-number-digit> ] |" | |
468 | echo " [ -s <4-number-digit> ] | [ -c <4-number-digit> <test- count>" | |
469 | echo " [ all ] [ -h | --help ] [ -l ]" | |
470 | echo "" | |
471 | echo "Valid tests: 0001-$MAX_TEST" | |
472 | echo "" | |
473 | echo " all Runs all tests (default)" | |
474 | echo " -t Run test ID the number amount of times is recommended" | |
475 | echo " -w Watch test ID run until it runs into an error" | |
768dc4e4 LR |
476 | echo " -s Run test ID once" |
477 | echo " -c Run test ID x test-count number of times" | |
d9c6a72d LR |
478 | echo " -l List all test ID list" |
479 | echo " -h|--help Help" | |
480 | echo | |
481 | echo "If an error every occurs execution will immediately terminate." | |
482 | echo "If you are adding a new test try using -w <test-ID> first to" | |
483 | echo "make sure the test passes a series of tests." | |
484 | echo | |
485 | echo Example uses: | |
486 | echo | |
487 | echo "${TEST_NAME}.sh -- executes all tests" | |
488 | echo "${TEST_NAME}.sh -t 0008 -- Executes test ID 0008 number of times is recomended" | |
489 | echo "${TEST_NAME}.sh -w 0008 -- Watch test ID 0008 run until an error occurs" | |
490 | echo "${TEST_NAME}.sh -s 0008 -- Run test ID 0008 once" | |
491 | echo "${TEST_NAME}.sh -c 0008 3 -- Run test ID 0008 three times" | |
492 | echo | |
493 | list_tests | |
494 | exit 1 | |
495 | } | |
496 | ||
497 | function test_num() | |
498 | { | |
499 | re='^[0-9]+$' | |
500 | if ! [[ $1 =~ $re ]]; then | |
501 | usage | |
502 | fi | |
503 | } | |
504 | ||
505 | function get_test_count() | |
506 | { | |
507 | test_num $1 | |
508 | TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}') | |
509 | LAST_TWO=${TEST_DATA#*:*} | |
510 | echo ${LAST_TWO%:*} | |
511 | } | |
512 | ||
513 | function get_test_enabled() | |
514 | { | |
515 | test_num $1 | |
516 | TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}') | |
517 | echo ${TEST_DATA#*:*:} | |
518 | } | |
519 | ||
520 | function run_all_tests() | |
521 | { | |
522 | for i in $ALL_TESTS ; do | |
523 | TEST_ID=${i%:*:*} | |
524 | ENABLED=$(get_test_enabled $TEST_ID) | |
525 | TEST_COUNT=$(get_test_count $TEST_ID) | |
526 | if [[ $ENABLED -eq "1" ]]; then | |
527 | test_case $TEST_ID $TEST_COUNT | |
528 | fi | |
529 | done | |
530 | } | |
531 | ||
532 | function watch_log() | |
533 | { | |
534 | if [ $# -ne 3 ]; then | |
535 | clear | |
536 | fi | |
537 | date | |
538 | echo "Running test: $2 - run #$1" | |
539 | } | |
540 | ||
541 | function watch_case() | |
542 | { | |
543 | i=0 | |
544 | while [ 1 ]; do | |
545 | ||
546 | if [ $# -eq 1 ]; then | |
547 | test_num $1 | |
548 | watch_log $i ${TEST_NAME}_test_$1 | |
549 | ${TEST_NAME}_test_$1 | |
550 | else | |
551 | watch_log $i all | |
552 | run_all_tests | |
553 | fi | |
554 | let i=$i+1 | |
555 | done | |
556 | } | |
557 | ||
558 | function test_case() | |
559 | { | |
560 | NUM_TESTS=$DEFAULT_NUM_TESTS | |
561 | if [ $# -eq 2 ]; then | |
562 | NUM_TESTS=$2 | |
563 | fi | |
564 | ||
565 | i=0 | |
566 | while [ $i -lt $NUM_TESTS ]; do | |
567 | test_num $1 | |
568 | watch_log $i ${TEST_NAME}_test_$1 noclear | |
569 | RUN_TEST=${TEST_NAME}_test_$1 | |
570 | $RUN_TEST | |
571 | let i=$i+1 | |
572 | done | |
573 | } | |
574 | ||
575 | function parse_args() | |
576 | { | |
577 | if [ $# -eq 0 ]; then | |
578 | run_all_tests | |
579 | else | |
580 | if [[ "$1" = "all" ]]; then | |
581 | run_all_tests | |
582 | elif [[ "$1" = "-w" ]]; then | |
583 | shift | |
584 | watch_case $@ | |
585 | elif [[ "$1" = "-t" ]]; then | |
586 | shift | |
587 | test_num $1 | |
588 | test_case $1 $(get_test_count $1) | |
589 | elif [[ "$1" = "-c" ]]; then | |
590 | shift | |
591 | test_num $1 | |
592 | test_num $2 | |
593 | test_case $1 $2 | |
594 | elif [[ "$1" = "-s" ]]; then | |
595 | shift | |
596 | test_case $1 1 | |
597 | elif [[ "$1" = "-l" ]]; then | |
598 | list_tests | |
599 | elif [[ "$1" = "-h" || "$1" = "--help" ]]; then | |
600 | usage | |
601 | else | |
602 | usage | |
603 | fi | |
604 | fi | |
605 | } | |
606 | ||
607 | test_reqs | |
608 | allow_user_defaults | |
609 | load_req_mod | |
610 | ||
611 | trap "test_finish" EXIT | |
612 | ||
613 | parse_args $@ | |
614 | ||
615 | exit 0 |