1 /* Miscellaneous system calls. Author: Kees J. Bot
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
14 #include <minix/callnr.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>
25 #include <machine/archtypes.h>
29 #include "kernel/proc.h"
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)) */
38 "i386", /* machine (cpu) type */
39 #elif defined(__arm__)
40 "evbarm", /* machine (cpu) type */
42 #error /* oops, no 'uname -mk' */
46 static char *uts_tbl
[] = {
48 "i386", /* architecture */
49 #elif defined(__arm__)
50 "evbarm", /* architecture */
52 NULL
, /* No kernel architecture */
54 NULL
, /* No hostname */
59 NULL
, /* No bus */ /* No bus */
61 /* END OF COMPATIBILITY BLOCK */
63 #if ENABLE_SYSCALL_STATS
64 unsigned long calls_stats
[NR_PM_CALLS
];
67 /* START OF COMPATIBILITY BLOCK */
68 /*===========================================================================*
70 *===========================================================================*/
74 /* Set or get uname strings. */
79 if (m_in
.m_lc_pm_sysuname
.field
>= __arraycount(uts_tbl
)) return(EINVAL
);
81 string
= uts_tbl
[m_in
.m_lc_pm_sysuname
.field
];
83 return EINVAL
; /* Unsupported field */
85 switch (m_in
.m_lc_pm_sysuname
.req
) {
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
);
98 /* Return the number of bytes moved. */
101 /* END OF COMPATIBILITY BLOCK */
104 /*===========================================================================*
106 *===========================================================================*/
110 vir_bytes src_addr
, dst_addr
;
113 /* This call leaks important information. In the future, requests from
114 * non-system processes should be denied.
116 if (mp
->mp_effuid
!= 0)
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
);
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
;
129 #if ENABLE_SYSCALL_STATS
131 src_addr
= (vir_bytes
) calls_stats
;
132 len
= sizeof(calls_stats
);
139 if (len
!= m_in
.m_lsys_getsysinfo
.size
)
142 dst_addr
= m_in
.m_lsys_getsysinfo
.where
;
143 return sys_datacopy(SELF
, src_addr
, who_e
, dst_addr
, len
);
146 /*===========================================================================*
148 *===========================================================================*/
149 int do_getprocnr(void)
151 register struct mproc
*rmp
;
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
);
159 if ((rmp
= find_proc(m_in
.m_lsys_pm_getprocnr
.pid
)) == NULL
)
162 mp
->mp_reply
.m_pm_lsys_getprocnr
.endpt
= rmp
->mp_endpoint
;
166 /*===========================================================================*
168 *===========================================================================*/
169 int do_getepinfo(void)
173 int r
, slot
, ngroups
;
175 ep
= m_in
.m_lsys_pm_getepinfo
.endpt
;
176 if (pm_isokendpt(ep
, &slot
) != OK
)
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
;
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
)
195 /*===========================================================================*
197 *===========================================================================*/
203 /* Check permission to abort the system. */
204 if (mp
->mp_effuid
!= SUPER_USER
) return(EPERM
);
206 /* See how the system should be aborted. */
207 abort_flag
= m_in
.m_lc_pm_reboot
.how
;
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
);
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.
223 check_sig(-1, SIGKILL
, FALSE
/* ksig*/); /* kill all users except init */
224 sys_stop(INIT_PROC_NR
); /* stop init, but keep it around */
226 /* Tell VFS to reboot */
227 memset(&m
, 0, sizeof(m
));
228 m
.m_type
= VFS_PM_REBOOT
;
230 tell_vfs(&mproc
[VFS_PROC_NR
], &m
);
232 return(SUSPEND
); /* don't reply to caller */
235 /*===========================================================================*
236 * do_getsetpriority *
237 *===========================================================================*/
239 do_getsetpriority(void)
241 int r
, arg_which
, arg_who
, arg_pri
;
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 */
248 /* Code common to GETPRIORITY and SETPRIORITY. */
250 /* Only support PRIO_PROCESS for now. */
251 if (arg_which
!= PRIO_PROCESS
)
257 if ((rmp
= find_proc(arg_who
)) == NULL
)
260 if (mp
->mp_effuid
!= SUPER_USER
&&
261 mp
->mp_effuid
!= rmp
->mp_effuid
&& mp
->mp_effuid
!= rmp
->mp_realuid
)
264 /* If GET, that's it. */
265 if (call_nr
== PM_GETPRIORITY
) {
266 return(rmp
->mp_nice
- PRIO_MIN
);
269 /* Only root is allowed to reduce the nice level. */
270 if (rmp
->mp_nice
> arg_pri
&& mp
->mp_effuid
!= SUPER_USER
)
273 /* We're SET, and it's allowed.
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.
280 if ((r
= sched_nice(rmp
, arg_pri
)) != OK
) {
284 rmp
->mp_nice
= arg_pri
;
288 /*===========================================================================*
290 *===========================================================================*/
296 #define MAX_LOCAL_PARAMS 2
300 } local_param_overrides
[MAX_LOCAL_PARAMS
];
301 static int local_params
= 0;
303 req
= m_in
.m_lc_svrctl
.request
;
304 ptr
= m_in
.m_lc_svrctl
.arg
;
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
);
309 /* Control operations local to the PM. */
315 struct sysgetenv sysgetenv
;
321 /* Copy sysgetenv structure to PM. */
322 if (sys_datacopy(who_e
, ptr
, SELF
, (vir_bytes
) &sysgetenv
,
323 sizeof(sysgetenv
)) != OK
) return(EFAULT
);
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
))
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
)
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
)
344 local_param_overrides
[local_params
].name
[sysgetenv
.keylen
] = '\0';
345 local_param_overrides
[local_params
].value
[sysgetenv
.vallen
] = '\0';
352 if (sysgetenv
.keylen
== 0) { /* copy all parameters */
353 val_start
= monitor_params
;
354 val_len
= sizeof(monitor_params
);
356 else { /* lookup value for key */
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
)
364 /* Make sure key is null-terminated and lookup value.
365 * First check local overrides.
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
;
374 if (p
>= local_params
&& (val_start
= find_param(search_key
)) == NULL
)
376 val_len
= strlen(val_start
) + 1;
379 /* See if it fits in the client's buffer. */
380 if (val_len
> sysgetenv
.vallen
)
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
)
397 /*===========================================================================*
399 *===========================================================================*/
403 clock_t user_time
, sys_time
;
404 struct rusage r_usage
;
407 if (m_in
.m_lc_pm_rusage
.who
!= RUSAGE_SELF
&&
408 m_in
.m_lc_pm_rusage
.who
!= RUSAGE_CHILDREN
)
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.
419 memset(&r_usage
, 0, sizeof(r_usage
));
421 children
= (m_in
.m_lc_pm_rusage
.who
== RUSAGE_CHILDREN
);
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.
429 if ((r
= sys_times(who_e
, &user_time
, &sys_time
, NULL
,
433 user_time
= mp
->mp_child_utime
;
434 sys_time
= mp
->mp_child_stime
;
437 /* In both cases, convert from clock ticks to microseconds. */
438 set_rusage_times(&r_usage
, user_time
, sys_time
);
440 /* Get additional fields from VM. */
441 if ((r
= vm_getrusage(who_e
, &r_usage
, children
)) != OK
)
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
));