#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void sys__exit(int exitcode) { struct addrspace * as; struct proc * p = curproc; if (exitcode == -2) p->exitcode = _MKWAIT_SIG(129); // not sure what error code is normally, but this is unix std else p->exitcode = _MKWAIT_EXIT(exitcode); DEBUG(DB_SYSCALL,"Syscall: _exit(%d)\n",exitcode); KASSERT(curproc->p_addrspace != NULL); lock_acquire(proclock); int x = listpop(p->kids); while (x) { if(processes->pids[x]) { processes->pids[x]->parent = NULL; if (processes->pids[x]->exitcode >= 0) { lock_release(proclock); proc_destroy(processes->pids[x]); lock_acquire(proclock); } } x = listpop(p->kids); } lock_release(proclock); listelete(p->kids); // VFS fields if (p->p_cwd) { VOP_DECREF(p->p_cwd); p->p_cwd = NULL; } as_deactivate(); as = curproc_setas(NULL); as_destroy(as); /* detach this thread from its process */ /* note: curproc cannot be used after this call */ proc_remthread(curthread); threadarray_cleanup(&p->p_threads); spinlock_cleanup(&p->p_lock); lock_acquire(p->waitlock); cv_broadcast(p->waiting, p->waitlock); lock_release(p->waitlock); if (!(p->parent) || p->parent->exitcode >= 0 || p->parent == kproc) proc_destroy(p); thread_exit(); /* thread_exit() does not return, so we should never get here */ panic("return from thread_exit in sys_exit\n"); } // basically, return an error code, and put the actual result in retval int sys_getpid(pid_t * retval) { if (!(curproc)) return 1; *retval = curproc->pid; return(0); } int sys_waitpid(pid_t pid, userptr_t status, int options, pid_t * retval) { if (options != 0 || !(void *)(status)) { return(EINVAL); } if (pid < PID_MIN || pid > PID_MAX) return ESRCH; struct proc * target = getChild(curproc, pid); if (!(target)) return ECHILD; if (!(target->exitcode >= 0)) { lock_acquire(target->waitlock); cv_wait(target->waiting, target->waitlock); lock_release(target->waitlock); } int exitstatus = target->exitcode; *retval = target->exitcode; int result = copyout((void *)&exitstatus, status, sizeof(int)); if (result) { return result; } proc_destroy(target); return 0; } int sys_fork(struct trapframe * tf, int * retval) { // create new process' memory space struct proc * child = proc_create_runprogram("childproc"); if ((!child)) return ENOMEM; struct trapframe * new_tf = kmalloc(sizeof(*tf)); struct addrspace * new_as; as_copy(curproc_getas(), &new_as); if (!(new_as) || !(new_tf)) { proc_destroy(child); kfree(new_as); kfree(new_tf); return ENOMEM; } // set PIDs, etc. copy data in to the new space child->p_addrspace = new_as; child->parent = curproc; add_child(curproc, child->pid); *new_tf = *tf; // start new thread thread_fork("childproc", child, enter_forked_process, new_tf, 0); // return correct values if (curproc->pid == child->pid) *retval = 0; else *retval = child->pid; return 0; } // this function takes an argc, argv, and puts argv into the userptr argvaddr, on the stack stackptr int copyArgs(int argc, char ** argv, userptr_t * argvAddr, vaddr_t * stackptr) { vaddr_t stack = *stackptr; // for ease of referencing char ** newArgv = kmalloc(sizeof(char *) * (argc + 1)); // need argc + 1 space since we want a final address to be null size_t wasteOfSpace; int errcode; for(int i = 0; i < argc; ++i) { int arglen = strlen(*(argv + i)) + 1; // length of char array for this arg stack -= ROUNDUP(arglen, 8); // make the stack bigger by this length, but rounded to 8 cause bytes errcode = copyoutstr(*(argv + i), (userptr_t)stack, arglen, &wasteOfSpace); // take the string in argv at i, put it at the stack position we calculated just before as the start for a string of our length if(errcode) { kfree(newArgv); return errcode; // if an error, fuck life } *(newArgv + i) = (char *)stack; // our argv kernel array is going to contain the user space address } *(newArgv + argc) = NULL; // set final address to NULL for(int i = 0; i <= argc; ++i) // after we store the array contents, we want to store the array of pointers to those contents. which is "before" in the stack becuase fuck you, that's why { stack -= sizeof(char *); // move the stack pointer back one pointer worth of space errcode = copyout(newArgv + (argc - i), (userptr_t)stack, sizeof(char *)); // copy the pointers in reverse order into the stack, since we are going backwards in the stack. if(errcode) { kfree(newArgv); return errcode; // cry. just cry. then return your error. } } *argvAddr = (userptr_t)stack; // set the argv array in userland to start at where we put it (current stackptr location) if (stack % 8 == 0) stack -= 8; // move the stack pointer back the minimum amount (if we are at an 8 byte address already, a full 8 bytes. otherwise we must be at a 4 byte address, so just half that) else stack -= 4; *stackptr = stack; // set the real stack pointer to the one we've been fucking with kfree(newArgv); // all data copied over, can free our temp array now return 0; // return no error } static void freeArgv(char ** argv, int len) { for (int i = 0; i <= len; ++i) { kfree(argv[i]); } } // args is an array of null terminated strings, with the last element being a null pointer so we don't overflow if iterating // since this is a userptr type, we need to use copyin and copyout to get data properly // hopefully the path in program is actually a full filepath int sys_execv(const char * program, userptr_t args) { userptr_t temp = args; int argcount = 0; int errcode; // why don't we have errno!!! my beautful one-liners!!! // see how long args is while (1) { void * temp2 = kmalloc(sizeof(temp)); errcode = copyin(temp, temp2, sizeof(temp2)); if (errcode) return errcode; if (!(*(char **)temp2)) break; temp += sizeof(temp); kfree(temp2); ++argcount; } // allocate space for argv char ** argv = kmalloc(argcount * sizeof(char *)); if (!argv) return ENOMEM; // get kernel copy of args in argv temp = args; for (int i = 0; i < argcount; ++i) { char * useraddr = kmalloc(sizeof(temp)); if (!(useraddr)) { kfree(argv); return ENOMEM; } // useraddr is now the address of the string in userland errcode = copyin(temp, useraddr, sizeof(temp)); if (errcode) { kfree(argv); return errcode; } // change argv[i] to be a string of length enough int strLen = strlen(*(char **)useraddr) + 1; kfree(useraddr); size_t wasteOfSpace; argv[i] = kmalloc(strLen * sizeof(char)); if (!(argv[i])) { freeArgv(argv, i); kfree(argv); return ENOMEM; } errcode = copyinstr((userptr_t)*(char **)temp, argv[i], strLen, &wasteOfSpace); if (errcode) { freeArgv(argv, i); kfree(argv); return errcode; } temp += sizeof(temp); } // allocate space for filepath char * filepath = kmalloc((strlen(program) + 1) * sizeof(char)); if (!(filepath)) { freeArgv(argv, argcount - 1); kfree(argv); return ENOMEM; } // get kernel copy of filepath size_t useless; errcode = copyinstr((const_userptr_t)program, filepath, strlen(program) + 1, &useless); if (errcode) { freeArgv(argv, argcount - 1); kfree(argv); kfree(filepath); return errcode; } // open program file struct vnode * v; errcode = vfs_open(filepath, O_RDONLY, 0, &v); if (errcode) { freeArgv(argv, argcount - 1); kfree(argv); kfree(filepath); return errcode; } // create new address space struct addrspace * as = as_create(); if (!(as)) { vfs_close(v); freeArgv(argv, argcount - 1); kfree(argv); kfree(filepath); return ENOMEM; } // Switch to the new as and activate. keep old in case we fail later struct addrspace * oldas = curproc_getas(); curproc_setas(as); as_activate(); vaddr_t entrypoint; // Load santa's favourite slave errcode = load_elf(v, &entrypoint); if (errcode) { vfs_close(v); freeArgv(argv, argcount - 1); kfree(argv); kfree(filepath); curproc_setas(oldas); as_destroy(as); return errcode; } // close the file now that we're done vfs_close(v); // create the new stack vaddr_t newstack; errcode = as_define_stack(as, &newstack); if (errcode) { freeArgv(argv, argcount - 1); kfree(argv); kfree(filepath); curproc_setas(oldas); as_destroy(as); return errcode; } // put args into the stack userptr_t userV; errcode = copyArgs(argcount, argv, &userV, &newstack); if (errcode) { freeArgv(argv, argcount - 1); kfree(argv); kfree(filepath); curproc_setas(oldas); as_destroy(as); return errcode; } // delete old addrspace, enter new process as_destroy(oldas); kfree(filepath); freeArgv(argv, argcount - 1); kfree(argv); enter_new_process(argcount, userV, newstack, entrypoint); panic("Enter new process returned!\n"); return EINVAL; }