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