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 "arm", /* machine (cpu) type */ 41 "arm", /* 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