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