Overhaul lock_test.R
[akaros.git] / user / parlib / parlib.c
1 /* Copyright (c) 2015 Google, Inc.
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details. */
4
5 #include <parlib/parlib.h>
6 #include <parlib/core_set.h>
7 #include <parlib/ros_debug.h>
8 #include <parlib/event.h>
9 #include <stdlib.h>
10 #include <sys/wait.h>
11
12 /* Control variables */
13 bool parlib_wants_to_be_mcp = TRUE;
14 bool parlib_never_yield = FALSE;
15 bool parlib_never_vc_request = FALSE;
16
17 /* Creates a child process for program @exe, with args and envs.  Will attempt
18  * to look in /bin/ if the initial lookup fails, and will invoke sh to handle
19  * non-elfs.  Returns the child's PID on success, -1 o/w. */
20 pid_t create_child(const char *exe, int argc, char *const argv[],
21                    char *const envp[])
22 {
23         pid_t kid;
24         char *path_exe;
25         char **sh_argv;
26         const char *sh_path = "/bin/sh";
27
28         kid = sys_proc_create(exe, strlen(exe), argv, envp, 0);
29         if (kid > 0)
30                 return kid;
31
32         /* Here's how we avoid infinite recursion.  We can only have ENOENT the
33          * first time through without bailing out, since all errno paths set exe
34          * to begin with '/'.  That includes calls from ENOEXEC, since sh_path
35          * begins with /.  To avoid repeated calls to ENOEXEC, we just look for
36          * sh_path as the exe, so if we have consecutive ENOEXECs, we'll bail
37          * out. */
38         switch (errno) {
39         case ENOENT:
40                 if (exe[0] == '/')
41                         return -1;
42                 path_exe = malloc(MAX_PATH_LEN);
43                 if (!path_exe)
44                         return -1;
45                 /* Our 'PATH' is only /bin. */
46                 snprintf(path_exe, MAX_PATH_LEN, "/bin/%s", exe);
47                 path_exe[MAX_PATH_LEN - 1] = 0;
48                 kid = create_child(path_exe, argc, argv, envp);
49                 free(path_exe);
50                 break;
51         case ENOEXEC:
52                 /* In case someone replaces /bin/sh with a non-elf. */
53                 if (!strcmp(sh_path, exe))
54                         return -1;
55                 /* We want enough space for the original argv, plus one entry at
56                  * the front for sh_path.  When we grab the original argv, we
57                  * also need the trailing NULL, which is at argv[argc].  That
58                  * means we really want argc + 1 entries from argv. */
59                 sh_argv = malloc(sizeof(char *) * (argc + 2));
60                 if (!sh_argv)
61                         return -1;
62                 memcpy(&sh_argv[1], argv, sizeof(char *) * (argc + 1));
63                 sh_argv[0] = (char*)sh_path;
64                 /* Replace the original argv[0] with the path to exe, which
65                  * might have been edited to include /bin/ */
66                 sh_argv[1] = (char*)exe;
67                 kid = create_child(sh_path, argc + 1, sh_argv, envp);
68                 free(sh_argv);
69                 break;
70         default:
71                 return -1;
72         }
73         return kid;
74 }
75
76 /* Creates a child process for exe, and shares the parent's standard FDs (stdin,
77  * stdout, stderr) with the child.  Returns the child's PID on success, -1 o/w.
78  */
79 pid_t create_child_with_stdfds(const char *exe, int argc, char *const argv[],
80                                char *const envp[])
81 {
82         struct childfdmap fd_dups[3] = { {0, 0}, {1, 1}, {2, 2} };
83         pid_t kid;
84         int ret;
85
86         kid = create_child(exe, argc, argv, envp);
87         if (kid < 0)
88                 return -1;
89         ret = syscall(SYS_dup_fds_to, kid, fd_dups, COUNT_OF(fd_dups));
90         if (ret != COUNT_OF(fd_dups)) {
91                 sys_proc_destroy(kid, -1);
92                 return -1;
93         }
94         return kid;
95 }
96
97 /* Helper for kicking off a process, but with little specific error handling */
98 int run_and_wait(const char *exe, int argc, char *const argv[])
99 {
100         extern char **environ;
101
102         pid_t kid;
103
104         kid = create_child_with_stdfds(exe, argc, argv, environ);
105         if (kid < 0)
106                 return -1;
107         if (sys_proc_run(kid) < 0)
108                 return -1;
109         if (waitpid(kid, NULL, 0) != kid)
110                 return -1;
111         return 0;
112 }
113
114 /* Provisions the CG cores to PID.  Returns -1 if any of them fail. */
115 int provision_core_set(pid_t pid, const struct core_set *cores)
116 {
117         struct core_set pvcores;
118         size_t max_cores = parlib_nr_total_cores();
119
120         parlib_get_ll_core_set(&pvcores);
121         parlib_not_core_set(&pvcores);
122         parlib_and_core_sets(&pvcores, cores);
123         for (size_t i = 0; i < max_cores; i++) {
124                 if (parlib_get_core(&pvcores, i)) {
125                         if (sys_provision(pid, RES_CORES, i))
126                                 return -1;
127                 }
128         }
129         return 0;
130 }
131
132 /* This sets up 'handler' to be run when the process receives EV_FREE_APPLE_PIE
133  * (9).  You can send this event with the notify utility:
134  *
135  *      notify PID 9 [Arg1 Arg2 0xArg3 Arg4]
136  *
137  * A simple debug handler can switch on an arg:
138
139         static void notify_ipi(struct event_msg *ev_msg, unsigned int ev_type,
140                                void *data)
141         {
142                 switch (ev_msg->ev_arg1) {
143                 case 1:
144                         // do something
145                         break;
146                 case 2:
147                         // do something else
148                         break;
149                 default:
150                         printf("Unknown arg %d\n", ev_msg->ev_arg1);
151                         break;
152                 }
153         }
154
155  */
156 void set_notify_9(void (*handler)(struct event_msg *ev_msg,
157                                   unsigned int ev_type, void *data),
158                   void *data)
159 {
160         struct event_queue *evq;
161
162         register_ev_handler(EV_FREE_APPLE_PIE, handler, data);
163         evq = get_eventq(EV_MBOX_UCQ);
164         evq->ev_flags = EVENT_IPI | EVENT_INDIR | EVENT_SPAM_INDIR |
165                         EVENT_WAKEUP;
166         register_kevent_q(evq, EV_FREE_APPLE_PIE);
167 }