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