xref: /minix3/minix/servers/pm/misc.c (revision eda6f5931d42c77e1480347b1fc3eef2f8d33806)
1 /* Miscellaneous system calls.				Author: Kees J. Bot
2  *								31 Mar 2000
3  * The entry points into this file are:
4  *   do_reboot: kill all processes, then reboot system
5  *   do_getsysinfo: request copy of PM data structure  (Jorrit N. Herder)
6  *   do_getprocnr: lookup endpoint by process ID
7  *   do_getepinfo: get the pid/uid/gid of a process given its endpoint
8  *   do_getsetpriority: get/set process priority
9  *   do_svrctl: process manager control
10  */
11 
12 #include "pm.h"
13 #include <minix/callnr.h>
14 #include <signal.h>
15 #include <sys/svrctl.h>
16 #include <sys/reboot.h>
17 #include <sys/resource.h>
18 #include <sys/utsname.h>
19 #include <minix/com.h>
20 #include <minix/config.h>
21 #include <minix/sysinfo.h>
22 #include <minix/type.h>
23 #include <minix/ds.h>
24 #include <machine/archtypes.h>
25 #include <lib.h>
26 #include <assert.h>
27 #include "mproc.h"
28 #include "kernel/proc.h"
29 
30 struct utsname uts_val = {
31   OS_NAME,		/* system name */
32   "noname",		/* node/network name */
33   OS_RELEASE,		/* O.S. release (e.g. 3.3.0) */
34   OS_VERSION,		/* O.S. version (e.g. Minix 3.3.0 (GENERIC)) */
35 #if defined(__i386__)
36   "i386",		/* machine (cpu) type */
37   "i386",		/* architecture */
38 #elif defined(__arm__)
39   "arm",		/* machine (cpu) type */
40   "arm",		/* architecture */
41 #else
42 #error			/* oops, no 'uname -mk' */
43 #endif
44 };
45 
46 static char *uts_tbl[] = {
47   uts_val.arch,
48   NULL,			/* No kernel architecture */
49   uts_val.machine,
50   NULL,			/* No hostname */
51   uts_val.nodename,
52   uts_val.release,
53   uts_val.version,
54   uts_val.sysname,
55   NULL,			/* No bus */			/* No bus */
56 };
57 
58 #if ENABLE_SYSCALL_STATS
59 unsigned long calls_stats[NR_PM_CALLS];
60 #endif
61 
62 /*===========================================================================*
63  *				do_sysuname				     *
64  *===========================================================================*/
65 int do_sysuname()
66 {
67 /* Set or get uname strings. */
68   int r;
69   size_t n;
70   char *string;
71 #if 0 /* for updates */
72   char tmp[sizeof(uts_val.nodename)];
73   static short sizes[] = {
74 	0,	/* arch, (0 = read-only) */
75 	0,	/* kernel */
76 	0,	/* machine */
77 	0,	/* sizeof(uts_val.hostname), */
78 	sizeof(uts_val.nodename),
79 	0,	/* release */
80 	0,	/* version */
81 	0,	/* sysname */
82   };
83 #endif
84 
85   if (m_in.m_lc_pm_sysuname.field >= _UTS_MAX) return(EINVAL);
86 
87   string = uts_tbl[m_in.m_lc_pm_sysuname.field];
88   if (string == NULL)
89 	return EINVAL;	/* Unsupported field */
90 
91   switch (m_in.m_lc_pm_sysuname.req) {
92   case _UTS_GET:
93 	/* Copy an uname string to the user. */
94 	n = strlen(string) + 1;
95 	if (n > m_in.m_lc_pm_sysuname.len) n = m_in.m_lc_pm_sysuname.len;
96 	r = sys_datacopy(SELF, (vir_bytes)string, mp->mp_endpoint,
97 		m_in.m_lc_pm_sysuname.value, (phys_bytes)n);
98 	if (r < 0) return(r);
99 	break;
100 
101 #if 0	/* no updates yet */
102   case _UTS_SET:
103 	/* Set an uname string, needs root power. */
104 	len = sizes[m_in.m_lc_pm_sysuname.field];
105 	if (mp->mp_effuid != 0 || len == 0) return(EPERM);
106 	n = len < m_in.m_lc_pm_sysuname.len ? len : m_in.m_lc_pm_sysuname.len;
107 	if (n <= 0) return(EINVAL);
108 	r = sys_datacopy(mp->mp_endpoint, m_in.m_lc_pm_sysuname.value, SELF,
109 		(phys_bytes)tmp, (phys_bytes)n);
110 	if (r < 0) return(r);
111 	tmp[n-1] = 0;
112 	strcpy(string, tmp);
113 	break;
114 #endif
115 
116   default:
117 	return(EINVAL);
118   }
119   /* Return the number of bytes moved. */
120   return(n);
121 }
122 
123 
124 /*===========================================================================*
125  *				do_getsysinfo			       	     *
126  *===========================================================================*/
127 int do_getsysinfo()
128 {
129   vir_bytes src_addr, dst_addr;
130   size_t len;
131 
132   /* This call leaks important information. In the future, requests from
133    * non-system processes should be denied.
134    */
135   if (mp->mp_effuid != 0)
136   {
137 	printf("PM: unauthorized call of do_getsysinfo by proc %d '%s'\n",
138 		mp->mp_endpoint, mp->mp_name);
139 	sys_diagctl_stacktrace(mp->mp_endpoint);
140 	return EPERM;
141   }
142 
143   switch(m_in.m_lsys_getsysinfo.what) {
144   case SI_PROC_TAB:			/* copy entire process table */
145         src_addr = (vir_bytes) mproc;
146         len = sizeof(struct mproc) * NR_PROCS;
147         break;
148 #if ENABLE_SYSCALL_STATS
149   case SI_CALL_STATS:
150   	src_addr = (vir_bytes) calls_stats;
151   	len = sizeof(calls_stats);
152   	break;
153 #endif
154   default:
155   	return(EINVAL);
156   }
157 
158   if (len != m_in.m_lsys_getsysinfo.size)
159 	return(EINVAL);
160 
161   dst_addr = m_in.m_lsys_getsysinfo.where;
162   return sys_datacopy(SELF, src_addr, who_e, dst_addr, len);
163 }
164 
165 /*===========================================================================*
166  *				do_getprocnr			             *
167  *===========================================================================*/
168 int do_getprocnr(void)
169 {
170   register struct mproc *rmp;
171 
172   /* This check should be replaced by per-call ACL checks. */
173   if (who_e != RS_PROC_NR) {
174 	printf("PM: unauthorized call of do_getprocnr by %d\n", who_e);
175 	return EPERM;
176   }
177 
178   if ((rmp = find_proc(m_in.m_lsys_pm_getprocnr.pid)) == NULL)
179 	return(ESRCH);
180 
181   mp->mp_reply.m_pm_lsys_getprocnr.endpt = rmp->mp_endpoint;
182   return(OK);
183 }
184 
185 /*===========================================================================*
186  *				do_getepinfo			             *
187  *===========================================================================*/
188 int do_getepinfo(void)
189 {
190   struct mproc *rmp;
191   endpoint_t ep;
192   int slot;
193 
194   ep = m_in.m_lsys_pm_getepinfo.endpt;
195   if (pm_isokendpt(ep, &slot) != OK)
196 	return(ESRCH);
197 
198   rmp = &mproc[slot];
199   mp->mp_reply.m_pm_lsys_getepinfo.uid = rmp->mp_effuid;
200   mp->mp_reply.m_pm_lsys_getepinfo.gid = rmp->mp_effgid;
201   return(rmp->mp_pid);
202 }
203 
204 /*===========================================================================*
205  *				do_reboot				     *
206  *===========================================================================*/
207 int do_reboot()
208 {
209   message m;
210 
211   /* Check permission to abort the system. */
212   if (mp->mp_effuid != SUPER_USER) return(EPERM);
213 
214   /* See how the system should be aborted. */
215   abort_flag = m_in.m_lc_pm_reboot.how;
216 
217   /* notify readclock (some arm systems power off via RTC alarms) */
218   if (abort_flag & RB_POWERDOWN) {
219 	endpoint_t readclock_ep;
220 	if (ds_retrieve_label_endpt("readclock.drv", &readclock_ep) == OK) {
221 		message m; /* no params to set, nothing we can do if it fails */
222 		_taskcall(readclock_ep, RTCDEV_PWR_OFF, &m);
223 	}
224   }
225 
226   /* Order matters here. When VFS is told to reboot, it exits all its
227    * processes, and then would be confused if they're exited again by
228    * SIGKILL. So first kill, then reboot.
229    */
230 
231   check_sig(-1, SIGKILL, FALSE /* ksig*/); /* kill all users except init */
232   sys_stop(INIT_PROC_NR);		   /* stop init, but keep it around */
233 
234   /* Tell VFS to reboot */
235   memset(&m, 0, sizeof(m));
236   m.m_type = VFS_PM_REBOOT;
237 
238   tell_vfs(&mproc[VFS_PROC_NR], &m);
239 
240   return(SUSPEND);			/* don't reply to caller */
241 }
242 
243 /*===========================================================================*
244  *				do_getsetpriority			     *
245  *===========================================================================*/
246 int do_getsetpriority()
247 {
248 	int r, arg_which, arg_who, arg_pri;
249 	struct mproc *rmp;
250 
251 	arg_which = m_in.m_lc_pm_priority.which;
252 	arg_who = m_in.m_lc_pm_priority.who;
253 	arg_pri = m_in.m_lc_pm_priority.prio;	/* for SETPRIORITY */
254 
255 	/* Code common to GETPRIORITY and SETPRIORITY. */
256 
257 	/* Only support PRIO_PROCESS for now. */
258 	if (arg_which != PRIO_PROCESS)
259 		return(EINVAL);
260 
261 	if (arg_who == 0)
262 		rmp = mp;
263 	else
264 		if ((rmp = find_proc(arg_who)) == NULL)
265 			return(ESRCH);
266 
267 	if (mp->mp_effuid != SUPER_USER &&
268 	   mp->mp_effuid != rmp->mp_effuid && mp->mp_effuid != rmp->mp_realuid)
269 		return EPERM;
270 
271 	/* If GET, that's it. */
272 	if (call_nr == PM_GETPRIORITY) {
273 		return(rmp->mp_nice - PRIO_MIN);
274 	}
275 
276 	/* Only root is allowed to reduce the nice level. */
277 	if (rmp->mp_nice > arg_pri && mp->mp_effuid != SUPER_USER)
278 		return(EACCES);
279 
280 	/* We're SET, and it's allowed.
281 	 *
282 	 * The value passed in is currently between PRIO_MIN and PRIO_MAX.
283 	 * We have to scale this between MIN_USER_Q and MAX_USER_Q to match
284 	 * the kernel's scheduling queues.
285 	 */
286 
287 	if ((r = sched_nice(rmp, arg_pri)) != OK) {
288 		return r;
289 	}
290 
291 	rmp->mp_nice = arg_pri;
292 	return(OK);
293 }
294 
295 /*===========================================================================*
296  *				do_svrctl				     *
297  *===========================================================================*/
298 int do_svrctl(void)
299 {
300   unsigned long req;
301   int s;
302   vir_bytes ptr;
303 #define MAX_LOCAL_PARAMS 2
304   static struct {
305   	char name[30];
306   	char value[30];
307   } local_param_overrides[MAX_LOCAL_PARAMS];
308   static int local_params = 0;
309 
310   req = m_in.m_lc_svrctl.request;
311   ptr = m_in.m_lc_svrctl.arg;
312 
313   /* Is the request indeed for the PM? ('M' is old and being phased out) */
314   if (IOCGROUP(req) != 'P' && IOCGROUP(req) != 'M') return(EINVAL);
315 
316   /* Control operations local to the PM. */
317   switch(req) {
318   case OPMSETPARAM:
319   case OPMGETPARAM:
320   case PMSETPARAM:
321   case PMGETPARAM: {
322       struct sysgetenv sysgetenv;
323       char search_key[64];
324       char *val_start;
325       size_t val_len;
326       size_t copy_len;
327 
328       /* Copy sysgetenv structure to PM. */
329       if (sys_datacopy(who_e, ptr, SELF, (vir_bytes) &sysgetenv,
330               sizeof(sysgetenv)) != OK) return(EFAULT);
331 
332       /* Set a param override? */
333       if (req == PMSETPARAM || req == OPMSETPARAM) {
334   	if (local_params >= MAX_LOCAL_PARAMS) return ENOSPC;
335   	if (sysgetenv.keylen <= 0
336   	 || sysgetenv.keylen >=
337   	 	 sizeof(local_param_overrides[local_params].name)
338   	 || sysgetenv.vallen <= 0
339   	 || sysgetenv.vallen >=
340   	 	 sizeof(local_param_overrides[local_params].value))
341   		return EINVAL;
342 
343           if ((s = sys_datacopy(who_e, (vir_bytes) sysgetenv.key,
344             SELF, (vir_bytes) local_param_overrides[local_params].name,
345                sysgetenv.keylen)) != OK)
346                	return s;
347           if ((s = sys_datacopy(who_e, (vir_bytes) sysgetenv.val,
348             SELF, (vir_bytes) local_param_overrides[local_params].value,
349               sysgetenv.vallen)) != OK)
350                	return s;
351             local_param_overrides[local_params].name[sysgetenv.keylen] = '\0';
352             local_param_overrides[local_params].value[sysgetenv.vallen] = '\0';
353 
354   	local_params++;
355 
356   	return OK;
357       }
358 
359       if (sysgetenv.keylen == 0) {	/* copy all parameters */
360           val_start = monitor_params;
361           val_len = sizeof(monitor_params);
362       }
363       else {				/* lookup value for key */
364       	  int p;
365           /* Try to get a copy of the requested key. */
366           if (sysgetenv.keylen > sizeof(search_key)) return(EINVAL);
367           if ((s = sys_datacopy(who_e, (vir_bytes) sysgetenv.key,
368                   SELF, (vir_bytes) search_key, sysgetenv.keylen)) != OK)
369               return(s);
370 
371           /* Make sure key is null-terminated and lookup value.
372            * First check local overrides.
373            */
374           search_key[sysgetenv.keylen-1]= '\0';
375           for(p = 0; p < local_params; p++) {
376           	if (!strcmp(search_key, local_param_overrides[p].name)) {
377           		val_start = local_param_overrides[p].value;
378           		break;
379           	}
380           }
381           if (p >= local_params && (val_start = find_param(search_key)) == NULL)
382                return(ESRCH);
383           val_len = strlen(val_start) + 1;
384       }
385 
386       /* See if it fits in the client's buffer. */
387       if (val_len > sysgetenv.vallen)
388       	return E2BIG;
389 
390       /* Value found, make the actual copy (as far as possible). */
391       copy_len = MIN(val_len, sysgetenv.vallen);
392       if ((s=sys_datacopy(SELF, (vir_bytes) val_start,
393               who_e, (vir_bytes) sysgetenv.val, copy_len)) != OK)
394           return(s);
395 
396       return OK;
397   }
398 
399   default:
400 	return(EINVAL);
401   }
402 }
403 
404 /*===========================================================================*
405  *				do_getrusage				     *
406  *===========================================================================*/
407 int do_getrusage()
408 {
409 	int res = 0;
410 	clock_t user_time = 0;
411 	clock_t sys_time = 0;
412 	struct rusage r_usage;
413 	u64_t usec;
414 	if (m_in.m_lc_pm_rusage.who != RUSAGE_SELF &&
415 		m_in.m_lc_pm_rusage.who != RUSAGE_CHILDREN)
416 		return EINVAL;
417 	if ((res = sys_getrusage(&r_usage, who_e)) < 0)
418 		return res;
419 
420 	if (m_in.m_lc_pm_rusage.who == RUSAGE_CHILDREN) {
421 		usec = mp->mp_child_utime * 1000000 / sys_hz();
422 		r_usage.ru_utime.tv_sec = usec / 1000000;
423 		r_usage.ru_utime.tv_usec = usec % 1000000;
424 		usec = mp->mp_child_stime * 1000000 / sys_hz();
425 		r_usage.ru_stime.tv_sec = usec / 1000000;
426 		r_usage.ru_stime.tv_usec = usec % 1000000;
427 	}
428 
429 	return sys_datacopy(SELF, (vir_bytes)&r_usage, who_e,
430 		m_in.m_lc_pm_rusage.addr, (vir_bytes) sizeof(r_usage));
431 }
432