/* Return the login name of the user, or NULL if it can't be determined. The returned pointer, if not NULL, is good only until the next call. */ char * getlogin (void) { static char login[1024]; /* XXX */ error_t err; if (err = __USEPORT (PROC, __proc_getlogin (port, login))) { errno = err; return NULL; } return login; }
error_t _hurd_check_ids (void) { if (! _hurd_id.valid) { inline void dealloc (__typeof (_hurd_id.gen) *p) { if (p->uids) { __vm_deallocate (__mach_task_self (), (vm_address_t) p->uids, p->nuids * sizeof (uid_t)); p->uids = NULL; } p->nuids = 0; if (p->gids) { __vm_deallocate (__mach_task_self (), (vm_address_t) p->gids, p->ngids * sizeof (gid_t)); p->gids = NULL; } p->ngids = 0; } error_t err; dealloc (&_hurd_id.gen); dealloc (&_hurd_id.aux); if (_hurd_id.rid_auth != MACH_PORT_NULL) { __mach_port_deallocate (__mach_task_self (), _hurd_id.rid_auth); _hurd_id.rid_auth = MACH_PORT_NULL; } if (err = __USEPORT (AUTH, __auth_getids (port, &_hurd_id.gen.uids, &_hurd_id.gen.nuids, &_hurd_id.aux.uids, &_hurd_id.aux.nuids, &_hurd_id.gen.gids, &_hurd_id.gen.ngids, &_hurd_id.aux.gids, &_hurd_id.aux.ngids))) return err; _hurd_id.valid = 1; } return 0; }
error_t __hurd_invoke_translator (file_t file, int flags, file_t *newport) { error_t err; enum retry_type doretry; char retryname[1024]; /* XXX string_t LOSES! */ err = __file_invoke_translator (file, flags, &doretry, retryname, newport); if (! err) err = __USEPORT (CRDIR, __hurd_file_name_lookup_retry (port, doretry, retryname, flags, 0, newport)); return err; }
/* Create a new session with the calling process as its leader. The process group IDs of the session and the calling process are set to the process ID of the calling process, which is returned. */ pid_t __setsid (void) { error_t err; unsigned int stamp; HURD_CRITICAL_BEGIN; __mutex_lock (&_hurd_dtable_lock); stamp = _hurd_pids_changed_stamp; /* Atomic fetch. */ /* Tell the proc server we want to start a new session. */ err = __USEPORT (PROC, __proc_setsid (port)); if (err) __mutex_unlock (&_hurd_dtable_lock); else { /* Punt our current ctty, and update the dtable accordingly. We hold the dtable lock from before the proc_setsid call through clearing the cttyid port and processing the dtable, so that we can be sure that it's all done by the time the signal thread processes the pgrp change notification. */ _hurd_locked_install_cttyid (MACH_PORT_NULL); /* Synchronize with the signal thread to make sure we have received and processed proc_newids before returning to the user. This is necessary to ensure that _hurd_pgrp (and thus the value returned by `getpgrp ()' in other threads) has been updated before we return. */ while (_hurd_pids_changed_stamp == stamp) { #ifdef noteven /* XXX we have no need for a mutex, but cthreads demands one. */ __condition_wait (&_hurd_pids_changed_sync, NULL); #else __swtch_pri (0); #endif } } HURD_CRITICAL_END; return err ? __hurd_fail (err) : _hurd_pgrp; }
kern_return_t __get_privileged_ports (mach_port_t *host_priv_ptr, device_t *device_master_ptr) { if ((host_priv_ptr && _hurd_host_priv == MACH_PORT_NULL) || (device_master_ptr && _hurd_device_master == MACH_PORT_NULL)) { error_t err; if (_hurd_ports) /* We have gotten some initial ports, so perhaps we have a proc server to talk to. */ err = __USEPORT (PROC, __proc_getprivports (port, &_hurd_host_priv, &_hurd_device_master)); else return MACH_SEND_INVALID_DEST; if (err) return err; } if (host_priv_ptr) { error_t err = _hurd_host_priv == MACH_PORT_NULL ? 0 : __mach_port_mod_refs (mach_task_self (), _hurd_host_priv, MACH_PORT_RIGHT_SEND, +1); if (err) return err; *host_priv_ptr = _hurd_host_priv; } if (device_master_ptr) { error_t err = _hurd_device_master == MACH_PORT_NULL ? 0 : __mach_port_mod_refs (mach_task_self (), _hurd_device_master, MACH_PORT_RIGHT_SEND, +1); if (err) return err; *device_master_ptr = _hurd_device_master; } return KERN_SUCCESS; }
/* Return at most NAME_LEN characters of the login name of the user in NAME. If it cannot be determined or some other error occurred, return the error code. Otherwise return 0. */ int __getlogin_r (char *name, size_t name_len) { string_t login; error_t err; if (err = __USEPORT (PROC, __proc_getlogin (port, login))) return errno = err; size_t len = __strnlen (login, sizeof login - 1) + 1; if (len > name_len) { errno = ERANGE; return errno; } memcpy (name, login, len); return 0; }
int __uname (struct utsname *uname) { error_t err; if (err = __USEPORT (PROC, __proc_uname (port, uname))) return __hurd_fail (err); /* Fill in the hostname, which the proc server doesn't know. */ err = errno; if (__gethostname (uname->nodename, sizeof uname->nodename) < 0) { if (errno == ENAMETOOLONG) /* Ignore the error of the buffer being too small. It is of fixed size, nothing to do about it. */ errno = err; else return -1; } return 0; }
void _hurd_exit (int status) { /* Give the proc server our exit status. */ __USEPORT (PROC, __proc_mark_exit (port, status, 0)); /* Commit suicide. */ __task_terminate (__mach_task_self ()); /* Perhaps the cached mach_task_self was bogus. */ __task_terminate ((__mach_task_self) ()); /* This sucker really doesn't want to die. */ while (1) { #ifdef LOSE LOSE; #else volatile const int zero = 0, one = 1; volatile int lossage = one / zero; #endif } }
kern_return_t _S_msg_describe_ports (mach_port_t msgport, mach_port_t refport, mach_port_t *ports, mach_msg_type_number_t nports, char **desc, mach_msg_type_number_t *desclen) { char *p, *end; if (__USEPORT (AUTH, msgport != port)) return EPERM; end = *desc + *desclen; p = *desc; while (nports-- > 0) { char this[200]; describe_port (this, *ports++); p = __stpncpy (p, this, end - p); if (p == end && p[-1] != '\0') return ENOMEM; } *desclen = p - *desc; return 0; }
int __setregid (gid_t rgid, gid_t egid) { auth_t newauth; error_t err; HURD_CRITICAL_BEGIN; __mutex_lock (&_hurd_id.lock); err = _hurd_check_ids (); if (!err) { /* Make a new auth handle which has RGID as the real gid, and EGID as the first element in the list of effective gids. */ gid_t *newgen, *newaux; size_t ngen, naux; newgen = _hurd_id.gen.gids; ngen = _hurd_id.gen.ngids; if (egid != -1) { if (_hurd_id.gen.ngids == 0) { /* No effective gids now. The new set will be just GID. */ newgen = &egid; ngen = 1; } else { _hurd_id.gen.gids[0] = egid; _hurd_id.valid = 0; } } newaux = _hurd_id.aux.gids; naux = _hurd_id.aux.ngids; if (rgid != -1) { if (_hurd_id.aux.ngids == 0) { newaux = &rgid; naux = 1; } else { _hurd_id.aux.gids[0] = rgid; _hurd_id.valid = 0; } } err = __USEPORT (AUTH, __auth_makeauth (port, NULL, MACH_MSG_TYPE_COPY_SEND, 0, _hurd_id.gen.uids, _hurd_id.gen.nuids, _hurd_id.aux.uids, _hurd_id.aux.nuids, newgen, ngen, newaux, naux, &newauth)); } __mutex_unlock (&_hurd_id.lock); HURD_CRITICAL_END; if (err) return __hurd_fail (err); /* Install the new handle and reauthenticate everything. */ err = __setauth (newauth); __mach_port_deallocate (__mach_task_self (), newauth); return err; }
/* Set the group ID of the calling process to UID. If the calling process is the super-user, the real and effective group IDs, and the saved set-group-ID to UID; if not, the effective group ID is set to GID. */ int __setgid (gid_t gid) { auth_t newauth; error_t err; HURD_CRITICAL_BEGIN; __mutex_lock (&_hurd_id.lock); err = _hurd_check_ids (); if (!err) { /* Make a new auth handle which has GID as the real gid, and as the first element in the list of effective gids. */ gid_t *newgen, *newaux, auxbuf[2]; size_t ngen, naux; if (_hurd_id.gen.ngids == 0) { /* No effective gids now. The new set will be just GID. */ newgen = &gid; ngen = 1; } else { _hurd_id.gen.gids[0] = gid; _hurd_id.valid = 0; newgen = _hurd_id.gen.gids; ngen = _hurd_id.gen.ngids; } newaux = _hurd_id.aux.gids; naux = _hurd_id.aux.ngids; if (_hurd_id.gen.nuids > 0 && _hurd_id.gen.uids[0] == 0) { /* We are root; set the real and saved IDs too. */ _hurd_id.valid = 0; if (_hurd_id.aux.ngids < 2) { newaux = auxbuf; naux = 2; } newaux[0] = newaux[1] = gid; } err = __USEPORT (AUTH, __auth_makeauth (port, NULL, MACH_MSG_TYPE_COPY_SEND, 0, _hurd_id.gen.uids, _hurd_id.gen.nuids, _hurd_id.aux.uids, _hurd_id.aux.nuids, newgen, ngen, newaux, naux, &newauth)); } __mutex_unlock (&_hurd_id.lock); HURD_CRITICAL_END; if (err) return __hurd_fail (err); /* Install the new handle and reauthenticate everything. */ err = __setauth (newauth); __mach_port_deallocate (__mach_task_self (), newauth); return err; }
/* Set the real user ID, effective user ID, and saved-set user ID, of the calling process to RUID, EUID, and SUID, respectively. */ int __setresuid (uid_t ruid, uid_t euid, uid_t suid) { auth_t newauth; error_t err; HURD_CRITICAL_BEGIN; __mutex_lock (&_hurd_id.lock); err = _hurd_check_ids (); if (!err) { /* Make a new auth handle which has EUID as the first element in the list of effective uids. */ uid_t *newgen, *newaux; uid_t auxs[2] = { ruid, suid }; size_t ngen, naux; newgen = _hurd_id.gen.uids; ngen = _hurd_id.gen.nuids; if (euid != -1) { if (_hurd_id.gen.nuids == 0) { /* No effective uids now. The new set will be just UID. */ newgen = &euid; ngen = 1; } else { _hurd_id.gen.uids[0] = euid; _hurd_id.valid = 0; } } newaux = _hurd_id.aux.uids; naux = _hurd_id.aux.nuids; if (ruid != -1) { if (_hurd_id.aux.nuids == 0) { newaux = &ruid; naux = 1; } else { _hurd_id.aux.uids[0] = ruid; _hurd_id.valid = 0; } } if (suid != -1) { if (ruid == -1) { if (_hurd_id.aux.nuids >= 1) auxs[0] = _hurd_id.aux.uids[0]; else if (_hurd_id.gen.nuids >= 1) auxs[0] = _hurd_id.gen.uids[0]; else /* Not even an effective UID. Fall back to the only UID we have. */ auxs[0] = suid; } if (_hurd_id.aux.nuids <= 1) { /* No saved uids now. The new set will be just UID. */ newaux = auxs; naux = 2; } else { _hurd_id.aux.uids[1] = suid; _hurd_id.valid = 0; } } err = __USEPORT (AUTH, __auth_makeauth (port, NULL, MACH_MSG_TYPE_COPY_SEND, 0, newgen, ngen, newaux, naux, _hurd_id.gen.gids, _hurd_id.gen.ngids, _hurd_id.aux.gids, _hurd_id.aux.ngids, &newauth)); } __mutex_unlock (&_hurd_id.lock); HURD_CRITICAL_END; if (err) return __hurd_fail (err); /* Install the new handle and reauthenticate everything. */ err = __setauth (newauth); __mach_port_deallocate (__mach_task_self (), newauth); return err; }
void _hurdsig_fault_init (void) { error_t err; struct machine_thread_state state; mach_port_t sigexc; /* Allocate a port to receive signal thread exceptions. We will move this receive right to the proc server. */ err = __mach_port_allocate (__mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &sigexc); assert_perror (err); err = __mach_port_allocate (__mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &forward_sigexc); assert_perror (err); /* Allocate a port to receive the exception msgs forwarded from the proc server. */ err = __mach_port_insert_right (__mach_task_self (), sigexc, sigexc, MACH_MSG_TYPE_MAKE_SEND); assert_perror (err); /* Set the queue limit for this port to just one. The proc server will notice if we ever get a second exception while one remains queued and unreceived, and decide we are hopelessly buggy. */ #ifdef MACH_PORT_RECEIVE_STATUS_COUNT { const mach_port_limits_t lim = { mpl_qlimit: 1 }; assert (MACH_PORT_RECEIVE_STATUS_COUNT == sizeof lim / sizeof (natural_t)); err = __mach_port_set_attributes (__mach_task_self (), forward_sigexc, MACH_PORT_RECEIVE_STATUS, (mach_port_info_t) &lim, MACH_PORT_RECEIVE_STATUS_COUNT); } #else err = __mach_port_set_qlimit (__mach_task_self (), forward_sigexc, 1); #endif assert_perror (err); /* This state will be restored when we fault. It runs the function above. */ memset (&state, 0, sizeof state); MACHINE_THREAD_STATE_SET_PC (&state, faulted); MACHINE_THREAD_STATE_SET_SP (&state, faultstack, sizeof faultstack); err = __USEPORT (PROC, __proc_handle_exceptions (port, sigexc, forward_sigexc, MACH_MSG_TYPE_MAKE_SEND, MACHINE_THREAD_STATE_FLAVOR, (natural_t *) &state, MACHINE_THREAD_STATE_COUNT)); assert_perror (err); /* Direct signal thread exceptions to the proc server. */ #ifdef THREAD_EXCEPTION_PORT err = __thread_set_special_port (_hurd_msgport_thread, THREAD_EXCEPTION_PORT, sigexc); #elif defined (EXC_MASK_ALL) __thread_set_exception_ports (_hurd_msgport_thread, EXC_MASK_ALL & ~(EXC_MASK_SYSCALL | EXC_MASK_MACH_SYSCALL | EXC_MASK_RPC_ALERT), sigexc, EXCEPTION_STATE_IDENTITY, MACHINE_THREAD_STATE); #else # error thread_set_exception_ports? #endif __mach_port_deallocate (__mach_task_self (), sigexc); assert_perror (err); }
/* Overlay TASK, executing FILE with arguments ARGV and environment ENVP. If TASK == mach_task_self (), some ports are dealloc'd by the exec server. ARGV and ENVP are terminated by NULL pointers. */ error_t _hurd_exec (task_t task, file_t file, char *const argv[], char *const envp[]) { error_t err; char *args, *env; size_t argslen, envlen; int ints[INIT_INT_MAX]; mach_port_t ports[_hurd_nports]; struct hurd_userlink ulink_ports[_hurd_nports]; file_t *dtable; unsigned int dtablesize, i; struct hurd_port **dtable_cells; struct hurd_userlink *ulink_dtable; struct hurd_sigstate *ss; mach_port_t *please_dealloc, *pdp; /* XXX needs to be hurdmalloc XXX */ if (err = __argz_create (argv, &args, &argslen)) return err; if (err = __argz_create (envp, &env, &envlen)) goto outargs; /* Load up the ports to give to the new program. */ for (i = 0; i < _hurd_nports; ++i) if (i == INIT_PORT_PROC && task != __mach_task_self ()) { /* This is another task, so we need to ask the proc server for the right proc server port for it. */ if (err = __USEPORT (PROC, __proc_task2proc (port, task, &ports[i]))) { while (--i > 0) _hurd_port_free (&_hurd_ports[i], &ulink_ports[i], ports[i]); goto outenv; } } else ports[i] = _hurd_port_get (&_hurd_ports[i], &ulink_ports[i]); /* Load up the ints to give the new program. */ for (i = 0; i < INIT_INT_MAX; ++i) switch (i) { case INIT_UMASK: ints[i] = _hurd_umask; break; case INIT_SIGMASK: case INIT_SIGIGN: case INIT_SIGPENDING: /* We will set these all below. */ break; case INIT_TRACEMASK: ints[i] = _hurdsig_traced; break; default: ints[i] = 0; } ss = _hurd_self_sigstate (); assert (! __spin_lock_locked (&ss->critical_section_lock)); __spin_lock (&ss->critical_section_lock); __spin_lock (&ss->lock); ints[INIT_SIGMASK] = ss->blocked; ints[INIT_SIGPENDING] = ss->pending; ints[INIT_SIGIGN] = 0; for (i = 1; i < NSIG; ++i) if (ss->actions[i].sa_handler == SIG_IGN) ints[INIT_SIGIGN] |= __sigmask (i); /* We hold the sigstate lock until the exec has failed so that no signal can arrive between when we pack the blocked and ignored signals, and when the exec actually happens. A signal handler could change what signals are blocked and ignored. Either the change will be reflected in the exec, or the signal will never be delivered. Setting the critical section flag avoids anything we call trying to acquire the sigstate lock. */ __spin_unlock (&ss->lock); /* Pack up the descriptor table to give the new program. */ __mutex_lock (&_hurd_dtable_lock); dtablesize = _hurd_dtable ? _hurd_dtablesize : _hurd_init_dtablesize; if (task == __mach_task_self ()) /* Request the exec server to deallocate some ports from us if the exec succeeds. The init ports and descriptor ports will arrive in the new program's exec_startup message. If we failed to deallocate them, the new program would have duplicate user references for them. But we cannot deallocate them ourselves, because we must still have them after a failed exec call. */ please_dealloc = __alloca ((_hurd_nports + (2 * dtablesize)) * sizeof (mach_port_t)); else please_dealloc = NULL; pdp = please_dealloc; if (_hurd_dtable != NULL) { dtable = __alloca (dtablesize * sizeof (dtable[0])); ulink_dtable = __alloca (dtablesize * sizeof (ulink_dtable[0])); dtable_cells = __alloca (dtablesize * sizeof (dtable_cells[0])); for (i = 0; i < dtablesize; ++i) { struct hurd_fd *const d = _hurd_dtable[i]; if (d == NULL) { dtable[i] = MACH_PORT_NULL; continue; } __spin_lock (&d->port.lock); if (d->flags & FD_CLOEXEC) { /* This descriptor is marked to be closed on exec. So don't pass it to the new program. */ dtable[i] = MACH_PORT_NULL; if (pdp && d->port.port != MACH_PORT_NULL) { /* We still need to deallocate the ports. */ *pdp++ = d->port.port; if (d->ctty.port != MACH_PORT_NULL) *pdp++ = d->ctty.port; } __spin_unlock (&d->port.lock); } else { if (pdp && d->ctty.port != MACH_PORT_NULL) /* All the elements of DTABLE are added to PLEASE_DEALLOC below, so we needn't add the port itself. But we must deallocate the ctty port as well as the normal port that got installed in DTABLE[I]. */ *pdp++ = d->ctty.port; dtable[i] = _hurd_port_locked_get (&d->port, &ulink_dtable[i]); dtable_cells[i] = &d->port; } } } else { dtable = _hurd_init_dtable; ulink_dtable = NULL; dtable_cells = NULL; } /* The information is all set up now. Try to exec the file. */ { if (pdp) { /* Request the exec server to deallocate some ports from us if the exec succeeds. The init ports and descriptor ports will arrive in the new program's exec_startup message. If we failed to deallocate them, the new program would have duplicate user references for them. But we cannot deallocate them ourselves, because we must still have them after a failed exec call. */ for (i = 0; i < _hurd_nports; ++i) *pdp++ = ports[i]; for (i = 0; i < dtablesize; ++i) *pdp++ = dtable[i]; } err = __file_exec (file, task, 0, args, argslen, env, envlen, dtable, MACH_MSG_TYPE_COPY_SEND, dtablesize, ports, MACH_MSG_TYPE_COPY_SEND, _hurd_nports, ints, INIT_INT_MAX, please_dealloc, pdp - please_dealloc, &_hurd_msgport, task == __mach_task_self () ? 1 : 0); } /* Release references to the standard ports. */ for (i = 0; i < _hurd_nports; ++i) if (i == INIT_PORT_PROC && task != __mach_task_self ()) __mach_port_deallocate (__mach_task_self (), ports[i]); else _hurd_port_free (&_hurd_ports[i], &ulink_ports[i], ports[i]); if (ulink_dtable != NULL) /* Release references to the file descriptor ports. */ for (i = 0; i < dtablesize; ++i) if (dtable[i] != MACH_PORT_NULL) _hurd_port_free (dtable_cells[i], &ulink_dtable[i], dtable[i]); /* Release lock on the file descriptor table. */ __mutex_unlock (&_hurd_dtable_lock); /* Safe to let signals happen now. */ _hurd_critical_section_unlock (ss); outargs: free (args); outenv: free (env); return err; }