static void test_proc_kill_all(){ dbg(DBG_TEST, "testing proc_kill_all when called from init proc\n"); test_proc_kill_all_func(NULL, NULL); dbg(DBG_TEST, "testing proc_kill_all when called from a different proc\n"); proc_t *test_proc = proc_create("proc_kill_all_func caller"); kthread_t *test_thread = kthread_create(test_proc, test_proc_kill_all_func, NULL, NULL); sched_make_runnable(test_thread); int status; pid_t retpid = do_waitpid(test_proc->p_pid, 0, &status); KASSERT(retpid == test_proc->p_pid); int i; for (i = 0; i < NUM_PROCS; i++){ pid_t retval = do_waitpid(-1, 0, &status); /* make sure we actually were able to wait on this pid, * meaning that it was properly killed in proc_kill_all */ KASSERT(retval > 0); } dbg(DBG_TESTPASS, "all proc_kill_all tests passed!\n"); }
static void test_do_waitpid(waitpid_type_t type){ proc_t *test_procs[NUM_PROCS]; kthread_t *test_threads[NUM_PROCS]; int i; for (i = 0; i < NUM_PROCS; i++){ test_procs[i] = proc_create("test proc"); test_threads[i] = kthread_create(test_procs[i], simple_function, i, NULL); sched_make_runnable(test_threads[i]); } int j; for (j = 0; j < NUM_PROCS; j++){ if (type == ANY){ int status; do_waitpid(-1, 0, &status); } else { int status; pid_t proc_pid = test_procs[j]->p_pid; pid_t waitpid_pid = do_waitpid(proc_pid, 0, &status); KASSERT(waitpid_pid == proc_pid); } } int k; for (k = 0; k < NUM_PROCS; k++){ proc_t *p = test_procs[k]; KASSERT(proc_lookup(p->p_pid) == NULL); /* make sure all children have been reparented */ KASSERT(list_empty(&p->p_children)); /* make sure that it is no longer in it's parent's * child list */ KASSERT(!in_child_list(p)); /* make sure it exited with the correct status */ KASSERT(p->p_status == 0); KASSERT(p->p_state == PROC_DEAD); KASSERT(sched_queue_empty(&p->p_wait)); } }
void * sunghan_test(int arg1, void *arg2) { int status; int proc_count = 0; pid_t proc_pid[3]; int i; dbg(DBG_TEST, ">>> Start running sunghan_test()...\n"); mynode.length = 0; kmutex_init(&mynode.my_mutex); sched_queue_init(&mynode.my_queue); proc_t *p1 = proc_create("add_node"); KASSERT(NULL != p1); kthread_t *thr1 = kthread_create(p1, add_my_node, 0, NULL); KASSERT(NULL != thr1); sched_make_runnable(thr1); proc_pid[proc_count++] = p1->p_pid; proc_t *p2 = proc_create("remove_node"); KASSERT(NULL != p2); kthread_t *thr2 = kthread_create(p2, remove_my_node, 0, NULL); KASSERT(NULL != thr2); sched_make_runnable(thr2); proc_pid[proc_count++] = p2->p_pid; proc_t *p3 = proc_create("watch_dog"); KASSERT(NULL != p3); kthread_t *thr3 = kthread_create(p3, watch_dog, 0, NULL); KASSERT(NULL != thr3); sched_make_runnable(thr3); proc_pid[proc_count++] = p3->p_pid; for (i=0; i<2; ++i) { do_waitpid(proc_pid[i], 0, &status); } sched_broadcast_on(&mynode.my_queue); do_waitpid(proc_pid[2], 0, &status); while (!do_waitpid(p2->p_pid, 0, &status)); dbg(DBG_TEST, "sunghan_test() terminated\n"); return NULL; }
static void test_normal_locking(){ dbg(DBG_TEST, "testing normal mutex behavior\n"); kmutex_t m; kmutex_init(&m); proc_t *kmutex_proc = proc_create("kmutex_test_proc"); kthread_t *kmutex_thread = kthread_create(kmutex_proc, lock_kmutex_func, NULL, (void *) &m); sched_make_runnable(kmutex_thread); kmutex_lock(&m); /* let kmutex_proc attempt to lock the mutex */ yield(); kmutex_unlock(&m); /* lock and unlock the mutex with nobody on it's wait queue */ kmutex_lock(&m); kmutex_unlock(&m); int status; do_waitpid(kmutex_proc->p_pid, 0, &status); dbg(DBG_TESTPASS, "normal kmutex tests passed!\n"); }
static void test_locking_and_cancelling(){ dbg(DBG_TEST, "testing kmutex behavior with cancellation\n"); kmutex_t m; kmutex_init(&m); proc_t *kmutex_proc = proc_create("kmutex_sleep_test_proc"); kthread_t *kmutex_thread = kthread_create(kmutex_proc, cancellable_lock_kmutex, NULL, (void *) &m); sched_make_runnable(kmutex_thread); kmutex_lock(&m); /* let kmutex_proc attempt to lock the mutex */ yield(); kthread_cancel(kmutex_thread, 0); kmutex_unlock(&m); int status; do_waitpid(kmutex_proc->p_pid, 0, &status); dbg(DBG_TESTPASS, "kmutex cancellation tests passed!\n"); }
static void *initproc_run(int arg1, void *arg2) { switch(curtest) { case 0: return NULL; case 1: processSetUp(); break; case 2: processSetUp(); break; case 3: processSetUp(); break; case 4: processSetUp(); break; case 5: processSetUp(); break; case 6: producer_consumer();break; case 7: deadlock();break; case 8: reader_writer();break; case 9: shellTest();break; case 10: dead_own(); break; case 11: vfs_test_setup(); break; case 12: add_tests(); break; case 13: ut_vmmap(); break; } int status; while(!list_empty(&curproc->p_children)) { pid_t child = do_waitpid(-1, 0, &status); /*dbg(DBG_INIT,"Process %d cleaned successfully\n", child);*/ } return NULL; }
/* waitpid(int pid, int *stat, int opt) */ int sys_waitpid(struct trap *tf){ int pid = (int)tf->ebx; int *stat = (int*)tf->ecx; int opt = (int)tf->edx; return do_waitpid(pid, stat, opt); }
/* * Should be called from the init proc */ static void test_proc_create(){ dbg(DBG_TEST, "testing proc_create\n"); proc_t *myproc = proc_create("myproc"); KASSERT(list_empty(&myproc->p_threads)); KASSERT(list_empty(&myproc->p_children)); KASSERT(sched_queue_empty(&myproc->p_wait)); KASSERT(myproc->p_pproc->p_pid == 1 && "created proc's parent isn't the init proc\n"); KASSERT(myproc->p_state == PROC_RUNNING); /* make sure it's in the proc list */ KASSERT(proc_lookup(myproc->p_pid) == myproc && "created proc not in proc list\n"); /* make sure it's in it's parent's child list */ KASSERT(in_child_list(myproc)); /* clean everything up */ kthread_t *mythread = kthread_create(myproc, simple_function, NULL, NULL); sched_make_runnable(mythread); int status; do_waitpid(myproc->p_pid, 0, &status); dbg(DBG_TESTPASS, "all proc_create tests passed!\n"); }
/** * Call waitpid with a -1 pid and print a message about any process that exits. * Returns the pid found, including -ECHILD when this process has no children. */ static pid_t wait_for_any() { int rv; pid_t pid; pid = do_waitpid(-1, 0, &rv); if ( pid != -ECHILD) dbg_print("child (%d) exited: %d\n", pid, rv); return pid; }
static void simple_read(rw_args_t read_args){ proc_t *rp = proc_create("ata_read_proc"); kthread_t *rt = kthread_create(rp, read_func, 0, (void *) &read_args); sched_make_runnable(rt); int status2; do_waitpid(rp->p_pid, 0, &status2); }
static void simple_write(rw_args_t write_args){ proc_t *wp = proc_create("ata_write_proc"); kthread_t *wt = kthread_create(wp, write_func, 0, (void *) &write_args); sched_make_runnable(wt); int status; do_waitpid(wp->p_pid, 0, &status); }
/** * Once we're inside of idleproc_run(), we are executing in the context of the * first process-- a real context, so we can finally begin running * meaningful code. * * This is the body of process 0. It should initialize all that we didn't * already initialize in kmain(), launch the init process (initproc_run), * wait for the init process to exit, then halt the machine. * * @param arg1 the first argument (unused) * @param arg2 the second argument (unused) */ static void * idleproc_run(int arg1, void *arg2) { int status; pid_t child; dbg_print("Made it to idleproc_run\n"); /* create init proc */ kthread_t *initthr = initproc_create(); init_call_all(); GDB_CALL_HOOK(initialized); /* Create other kernel threads (in order) */ #ifdef __VFS__ /* Once you have VFS remember to set the current working directory * of the idle and init processes */ /* Here you need to make the null, zero, and tty devices using mknod */ /* You can't do this until you have VFS, check the include/drivers/dev.h * file for macros with the device ID's you will need to pass to mknod */ #endif /* Finally, enable interrupts (we want to make sure interrupts * are enabled AFTER all drivers are initialized) */ intr_enable(); /* Run initproc */ sched_make_runnable(initthr); /* Now wait for it */ child = do_waitpid(-1, 0, &status); KASSERT(PID_INIT == child); #ifdef __MTP__ kthread_reapd_shutdown(); #endif #ifdef __VFS__ /* Shutdown the vfs: */ dbg_print("weenix: vfs shutdown...\n"); vput(curproc->p_cwd); if (vfs_shutdown()) panic("vfs shutdown FAILED!!\n"); #endif /* Shutdown the pframe system */ #ifdef __S5FS__ pframe_shutdown(); #endif dbg_print("\nweenix: halted cleanly!\n"); GDB_CALL_HOOK(shutdown); hard_shutdown(); return NULL; }
/** * Call do_waitpid with the process ID of the given process. Print a debug * message with the exiting process's status. */ static void wait_for_proc(proc_t *p) { int rv; pid_t pid; char pname[PROC_NAME_LEN]; strncpy(pname, p->p_comm, PROC_NAME_LEN); pid = do_waitpid(p->p_pid, 0, &rv); dbg_print("%s (%d) exited: %d\n", pname, pid, rv); }
int sunghan_deadlock(kshell_t *kshell, int argc,char **argv) { int status; proc_t *sunghan_deadlock_testproc = proc_create("sunghan_deadlock"); kthread_t *sunghan_deadlock_testthread = kthread_create(sunghan_deadlock_testproc,sunghan_deadlock_test,0,NULL); sched_make_runnable(sunghan_deadlock_testthread); KASSERT(kshell != NULL); dbg(DBG_INIT,"(GRADING1D)\n"); while(do_waitpid(-1,0,&status)!=-ECHILD); return 0; }
int sunghandeadlocktest(kshell_t *kshell, int argc, char **argv) { int status; proc_t *temp_proc; kthread_t *temp_thread; temp_proc = proc_create("sunghan deadlock test"); dbg(DBG_PRINT, "(GRADING1D 2): sunghan_deadlock_test() is invoked\n"); temp_thread = kthread_create(temp_proc,sunghan_deadlock_test,0,NULL); sched_make_runnable(temp_thread); /*Make the thread runnable*/ do_waitpid(temp_proc->p_pid,0,&status); /*waiting for the test to end*/ return 0; }
int faber(kshell_t *kshell, int argc,char **argv) { int status; proc_t *faberproc = proc_create("faber"); kthread_t *faberthread = kthread_create(faberproc,faber_thread_test,0,NULL); sched_make_runnable(faberthread); KASSERT(kshell != NULL); dbg(DBG_INIT,"(GRADING1C: Faber test is invoked)\n"); while(do_waitpid(-1,0,&status)!=-ECHILD); return 0; }
/* * Run multiple pairs of threads, each running make_dir_thread or rm_dir_thread * with a different parameter. One will be creating a directory full of files, * and the other will be deleting them. They do not move in lock step, so for * a few threads, say 4 or 5, the system is likely to run out of i-nodes. * * The calling format (from the kshell) is * directory_test <num>, where num is an integer. The number is parsed into an * int and pairs of threads with parameter 0..num are created. This code then * waits for them and prints each exit value. */ int faber_directory_test(kshell_t *ksh, int argc, char **argv) { KASSERT(NULL != ksh); proc_t *p = NULL; /* A process to run in */ kthread_t *thr = NULL; /* A thread to run in it */ char tname[TESTBUFLEN]; /* A thread name */ int pid = 0; /* Process ID from do_waitpid (who exited?) */ int lim = 1; /* Number of processes to run */ int rv = 0; /* Return values */ int i = 0; /* Scratch */ KASSERT(NULL != ksh); if ( argc > 1) { /* Oh, barf */ if ( sscanf(argv[1], "%d", &lim) != 1) lim = 1; /* A little bounds checking */ if ( lim < 1 || lim > 255 ) lim = 1; } /* Start pairs of processes */ for ( i = 0; i< lim; i++) { /* The maker process */ snprintf(tname, TESTBUFLEN, "thread%03d", i); p = proc_create(tname); KASSERT(NULL != p); thr = kthread_create(p, make_dir_thread, i, NULL); KASSERT(NULL != thr); sched_make_runnable(thr); /* The deleter process */ snprintf(tname, TESTBUFLEN, "rmthread%03d", i); p = proc_create(tname); KASSERT(NULL != p); thr = kthread_create(p, rm_dir_thread, i, NULL); KASSERT(NULL != thr); sched_make_runnable(thr); } /* Wait for children and report their error codes */ while ( ( pid = do_waitpid(-1, 0 , &rv) ) != -ECHILD) if ( rv < 0 ) kprintf(ksh, "Child %d: %d %s\n", pid, rv, strerror(-rv)); else kprintf(ksh, "Child %d: %d\n", pid, rv); return rv; }
static void test_do_waitpid_no_child(){ pid_t pid; /* find a PID that definitely isn't a child of curproc */ for (pid = 0; proc_lookup(pid) != NULL; pid++){} int status; pid_t returned_pid = do_waitpid(pid, 0, &status); KASSERT(returned_pid = -ECHILD); }
/* * Should be called from a new process */ static void * test_do_exit_and_do_waitpid(int arg1, void *arg2){ int status; dbg(DBG_TEST, "testing do_waitpid on an invalid PID\n"); KASSERT(do_waitpid(-1, 0, &status) == -ECHILD); dbg(DBG_TEST, "testing do_waitpid on an empty child list\n"); KASSERT(do_waitpid(5, 0, &status) == -ECHILD); dbg(DBG_TEST, "testing do_waitpid with pid == -1\n"); test_do_waitpid(ANY); dbg(DBG_TEST, "testing do waitpid with specific pids\n"); test_do_waitpid(SPECIFIC); dbg(DBG_TEST, "testing do_waitpid with non-child pid\n"); test_do_waitpid_no_child(); dbg(DBG_TESTPASS, "all do_waitpid tests passed!\n"); return NULL; }
/** * The init thread's function changes depending on how far along your Weenix is * developed. Before VM/FI, you'll probably just want to have this run whatever * tests you've written (possibly in a new process). After VM/FI, you'll just * exec "/sbin/init". * * Both arguments are unused. * * @param arg1 the first argument (unused) * @param arg2 the second argument (unused) */ static void * initproc_run(int arg1, void *arg2) { /*NOT_YET_IMPLEMENTED("PROCS: initproc_run");*/ kshell_add_command("fabertest", do_fabertest, "invoke do_fabertest() to test."); kshell_add_command("sunghan", do_sunghan, "invoke sunghan_test() to test."); kshell_add_command("deadlock", do_deadlock, "invoke sunghan_deadlock_test() to test."); kshell_t *kshell = kshell_create(0); if (NULL == kshell) panic("init: Couldn't create kernel shell\n"); while (kshell_execute_next(kshell)) { while(!list_empty(&curproc->p_children)) do_waitpid(-1,0,0); } kshell_destroy(kshell); return NULL; }
void LightProcess::runShadow() { std::string buf; pollfd pfd[1]; pfd[0].fd = m_afdt_fd; pfd[0].events = POLLIN; try { while (true) { int ret = poll(pfd, 1, -1); if (ret < 0 && errno == EINTR) { continue; } if (pfd[0].revents & POLLHUP) { // no more command can come in Logger::Error("Lost parent, LightProcess exiting"); break; } if (pfd[0].revents & POLLIN) { lwp_read(m_afdt_fd, buf); if (buf == "exit") { Logger::Verbose("LightProcess exiting upon request"); break; } else if (buf == "popen") { do_popen(m_afdt_fd); } else if (buf == "pclose") { do_pclose(m_afdt_fd); } else if (buf == "proc_open") { do_proc_open(m_afdt_fd); } else if (buf == "waitpid") { do_waitpid(m_afdt_fd); } else if (buf == "change_user") { do_change_user(m_afdt_fd); } else if (buf[0]) { Logger::Info("LightProcess got invalid command: %.20s", buf.c_str()); } } } } catch (const std::exception& e) { Logger::Error("LightProcess exiting due to exception: %s", e.what()); } catch (...) { Logger::Error("LightProcess exiting due to unknown exception"); } ::close(m_afdt_fd); _Exit(0); }
/** * The init thread's function changes depending on how far along your Weenix is * developed. Before VM/FI, you'll probably just want to have this run whatever * tests you've written (possibly in a new process). After VM/FI, you'll just * exec "/bin/init". * * Both arguments are unused. * * @param arg1 the first argument (unused) * @param arg2 the second argument (unused) */ static void * initproc_run(int arg1, void *arg2) { /* PROCS {{{ */ #ifdef __VM__ int status; dbg(DBG_INIT, "do_init!\n"); do_open("/dev/tty0", O_RDONLY); do_open("/dev/tty0", O_WRONLY); do_open("/dev/tty0", O_WRONLY); char *const argvec[] = { "foo", NULL }; char *const envvec[] = { "bar", NULL }; kernel_execve("/sbin/init", argvec, envvec); while (!do_waitpid(-1, 0, &status)); do_exit(0); #endif #ifdef __DRIVERS__ /* If we do not have VM yet, run the kernel shell */ kshell_t *kshell; /* Create kernel shell on TTY 0 */ kshell = kshell_create(0); if (NULL == kshell) panic("init: Couldn't create kernel shell\n"); while (kshell_execute_next(kshell)); kshell_destroy(kshell); #endif /* PROCS }}} */ #ifdef __VFS__ /*TEST VFS*/ vfstest_main(1,NULL); do_exit(0); #endif return NULL; }
void LightProcess::runShadow(int fdin, int fdout) { FILE *fin = fdopen(fdin, "r"); FILE *fout = fdopen(fdout, "w"); char buf[BUFFER_SIZE]; pollfd pfd[1]; pfd[0].fd = fdin; pfd[0].events = POLLIN; while (true) { int ret = poll(pfd, 1, -1); if (ret < 0 && errno == EINTR) { continue; } if (pfd[0].revents & POLLIN) { if (!fgets(buf, BUFFER_SIZE, fin)) buf[0] = '\0'; if (strncmp(buf, "exit", 4) == 0) { Logger::Info("LightProcess exiting upon request"); break; } else if (strncmp(buf, "popen", 5) == 0) { do_popen(fin, fout, m_afdt_fd); } else if (strncmp(buf, "pclose", 6) == 0) { do_pclose(fin, fout); } else if (strncmp(buf, "proc_open", 9) == 0) { do_proc_open(fin, fout, m_afdt_fd); } else if (strncmp(buf, "waitpid", 7) == 0) { do_waitpid(fin, fout); } else if (strncmp(buf, "change_user", 11) == 0) { do_change_user(fin, fout); } else if (buf[0]) { Logger::Info("LightProcess got invalid command: %.20s", buf); } } else if (pfd[0].revents & POLLHUP) { // no more command can come in Logger::Error("Lost parent, LightProcess exiting"); break; } } fclose(fin); fclose(fout); ::close(m_afdt_fd); remove(m_afdtFilename.c_str()); _Exit(0); }
int do_faber(kshell_t *kshell, int argc, char **argv) { /* * Shouldn't call a test function directly. * It's best to invoke it in a separate kernel process. */ proc_t *new_proc; kthread_t *new_thr; new_proc = proc_create("new"); new_thr = kthread_create(new_proc, faber_thread_test, 0, NULL); sched_make_runnable(new_thr); int status; pid_t child = do_waitpid(-1, 0, &status); return 0; }
void run_proc_tests(){ test_proc_create(); proc_t *waitpid_test_proc = proc_create("waitpid_test_proc"); kthread_t *waitpid_test_thread = kthread_create(waitpid_test_proc, test_do_exit_and_do_waitpid, NULL, NULL); sched_make_runnable(waitpid_test_thread); int status; do_waitpid(waitpid_test_proc->p_pid, 0, &status); test_kthread_cancel(); test_proc_kill(); test_proc_kill_all(); test_kmutex(); dbg(DBG_TESTPASS, "all proc-related tests passed!\n"); }
static void test_kthread_cancel(){ dbg(DBG_TEST, "testing kthread_cancel\n"); proc_t *test_proc = proc_create("kthread_cancel_test_proc"); kthread_t *test_thread = kthread_create(test_proc, sleep_function, NULL, (void *) &test_proc->p_wait); sched_make_runnable(test_thread); /* make sure the thread goes to sleep before we cancel it */ yield(); kthread_cancel(test_thread, (void *) 5); KASSERT(test_thread->kt_cancelled == 1); KASSERT((int) test_thread->kt_retval == 5); int status; do_waitpid(test_proc->p_pid, 0, &status); dbg(DBG_TESTPASS, "all kthread_cancel tests passed!\n"); }
static void test_proc_kill(){ dbg(DBG_TEST, "testing proc_kill\n"); proc_t *test_proc = proc_create("proc_kill_test_proc"); kthread_t *test_thread = kthread_create(test_proc, sleep_function, NULL, (void *) &test_proc->p_wait); sched_make_runnable(test_thread); yield(); proc_kill(test_proc, 7); KASSERT(test_thread->kt_cancelled == 1); KASSERT(test_thread->kt_retval == 0); KASSERT(test_proc->p_status == 7); int status; do_waitpid(test_proc->p_pid, 0, &status); dbg(DBG_TESTPASS, "all proc_kill tests passed!\n"); }
void LightProcess::runShadow(int fdin, int fdout) { FILE *fin = fdopen(fdin, "r"); FILE *fout = fdopen(fdout, "w"); char buf[BUFFER_SIZE]; pollfd pfd[1]; pfd[0].fd = fdin; pfd[0].events = POLLIN; while (true) { poll(pfd, 1, -1); if (pfd[0].revents & POLLHUP) { // no more command can come in break; } else if (pfd[0].revents & POLLIN) { if (!fgets(buf, BUFFER_SIZE, fin)) buf[0] = '\0'; if (strncmp(buf, "exit", 4) == 0) { break; } else if (strncmp(buf, "popen", 5) == 0) { do_popen(fin, fout, m_afdt_fd); } else if (strncmp(buf, "pclose", 6) == 0) { do_pclose(fin, fout); } else if (strncmp(buf, "proc_open", 9) == 0) { do_proc_open(fin, fout, m_afdt_fd); } else if (strncmp(buf, "waitpid", 7) == 0) { do_waitpid(fin, fout); } else if (strncmp(buf, "change_user", 11) == 0) { do_change_user(fin, fout); } } } fclose(fin); fclose(fout); ::close(m_afdt_fd); remove(m_afdtFilename.c_str()); exit(0); }
static pid_t sys_waitpid(waitpid_args_t *args) { int s, p; waitpid_args_t kargs; if (0 > copy_from_user(&kargs, args, sizeof(kargs))) { curthr->kt_errno = EFAULT; return -1; } if (0 > (p = do_waitpid(kargs.wpa_pid, kargs.wpa_options, &s))) { curthr->kt_errno = -p; return -1; } if (NULL != kargs.wpa_status && 0 > copy_to_user(kargs.wpa_status, &s, sizeof(int))) { curthr->kt_errno = EFAULT; return -1; } return p; }
/** * The init thread's function changes depending on how far along your Weenix is * developed. Before VM/FI, you'll probably just want to have this run whatever * tests you've written (possibly in a new process). After VM/FI, you'll just * exec "/bin/init". * * Both arguments are unused. * * @param arg1 the first argument (unused) * @param arg2 the second argument (unused) */ static void * initproc_run(int arg1, void *arg2) { dbg(DBG_CORE, "running initproc_run\n"); /* TESTS */ my_proctest(); my_drivertest(); my_s5fstest(); my_vmtest(); vfstest_main(1, NULL); #ifdef __VM__ int status; char *const argv[] = { "hello", NULL }; char *const envp[] = { "world", NULL }; do_open("/dev/tty0", O_RDONLY); do_open("/dev/tty0", O_WRONLY); do_open("/dev/tty0", O_WRONLY); kernel_execve("/sbin/init", argv, envp); while (!do_waitpid(-1, 0, &status)); do_exit(0); #endif /* KSHELL */ int err = 0; kshell_t *ksh = kshell_create(0); KASSERT(ksh && "did not create a kernel shell as expected"); while ((err = kshell_execute_next(ksh)) > 0); KASSERT(err == 0 && "kernel shell exited with an error\n"); kshell_destroy(ksh); return NULL; }