Example #1
0
void ProcessManager::WarnExcess()
{
    PROCTAB* proc = openproc(PROC_FILLARG | PROC_FILLSTAT | PROC_FILLMEM);
    proc_t * proc_info;

    Core::DebugLog("Looking for processes that exceed the hard or soft limit", 10);

    while (true)
    {
        proc_info = readproc(proc, NULL);
        if (proc_info == NULL)
        {
            break;
        }
        if (!Configuration::KillRoot && proc_info->euid == 0)
        {
            if (Configuration::Verbosity >= 6)
            {
                Core::DebugLog("Ignoring " + Core::int2String(proc_info->tid) + " owned by root", 6);
            }
            freeproc(proc_info);
            continue;
        }

        if (proc_info->tid == Configuration::pid && !Configuration::KillSelf)
        {
            Core::DebugLog("Ignoring " + Core::int2String(proc_info->tid) + " which is current instance of this daemon", 6);
            freeproc(proc_info);
            continue;
        }

        if (IgnoredId(proc_info->euid))
        {
            Core::DebugLog("Ignoring " + Core::int2String(proc_info->tid) + " owned by ignored account: " + Core::int2String(proc_info->euid), 2);
            freeproc(proc_info);
            continue;
        }

        // check if this process is using most memory
        if ( proc_info->resident * 4 > ((long)Configuration::HardMemoryLimitMB * 1024 ))
        {
            Core::Log("WARNING: Exceeded hard limit - process " + Name(proc_info));
        } else if ( proc_info->resident * 4 > ((long)Configuration::SoftMemoryLimitMB * 1024 ))
        {
            Core::Log("WARNING: Exceeded soft limit - process " + Name(proc_info));
        } else
        {
            if (Configuration::Verbosity > 12)
            {
                Core::DebugLog("Not exceeded any limit " + Name(proc_info));
            }
        }
        freeproc(proc_info);
    }

    closeproc(proc);
    return;
}
Example #2
0
/*
 *	Pidof functionality.
 */
int affix_pidof(char *name, int flags, pid_t pid)
{
	PROC	*p;
	PIDQ	*q;
	int	i,oind;
	pid_t	opid[PIDOF_OMITSZ], spid = 0;
	char	*basec = NULL;

	for (oind = PIDOF_OMITSZ-1; oind > 0; oind--)
		opid[oind] = 0;

	if (flags&PIDOF_SCRIPTS)
		scripts_too++;

	if (flags&PIDOF_OMIT) {
		opid[oind] = pid;
		oind++;
	}
	if (flags&PIDOF_POMIT) {
		opid[oind] = getppid();
		oind++;
	}
	if (flags&PIDOF_BASENAME) {
		char	*ch;

		basec = strdup(name);
		name = basename(basec);
		if ((ch = strchr(name, ' '))) {
			*ch = '\0';
		}
	}
	/* Print out process-ID's one by one. */
	readproc();
	if ((q = pidof(name)) == NULL)
		goto exit;
	while ((p = get_next_from_pid_q(q))) {
		if (flags & PIDOF_OMIT) {
			for (i = 0; i < oind; i++) {
				if (opid[i] == p->pid)
					break;
			}
			/*
			 *	On a match, continue with
			 *	the for loop above.
			 */
			if (i < oind)
				continue;
		}
		if (flags & PIDOF_SINGLE) {
			if (spid)
				continue;
			else
				spid = p->pid;
		}
	}
exit:
	free(basec);
	freeproc();
	return spid;
}
Example #3
0
File: t.c Project: dabbers/dabos
int do_wait( int *status )
{
	int children, i;
	
	children = 0;

	for( i = 0; i < NPROC; i++ )
	{
		if ( proc[i].ppid == running->pid )
		{
			children++;
		}
	}

	while( children )
	{
		for (i = 0; i < NPROC; i++)
		{
			if (proc[i].status == ZOMBIE && proc[i].ppid == running->pid)
			{
				*status = proc[i].exitCode;
				proc[i].status = FREE;
				freeproc(&proc[i]);
				return i;
			}
		}
		ksleep(running);
	}

	return -1;
}
Example #4
0
/**
 * @brief deallocate the space allocated by readproc if the passed rbuf was NULL
 *
 * @param p The rbuf to free
 */
void standard_freeproc(proc_t* p) {
  if (!p) { // in case p is NULL
    return;
  }

#ifdef PROC_EDITCMDLCVT
  freeproc(p);
  return;
#endif

  // ptrs are after strings to avoid copying memory when building them.
  // so free is called on the address of the address of strvec[0].
  if (p->cmdline) {
    free((void*)*p->cmdline);
  }
  if (p->environ) {
    free((void*)*p->environ);
  }
  free(p);
}
Example #5
0
//! Kill process which is eating most
void ProcessManager::KillHighest(bool hard)
{
    PROCTAB* proc = openproc(PROC_FILLARG | PROC_FILLSTAT | PROC_FILLMEM);
    proc_t* proc_info;
    // zero out the allocated proc_info memory
    //memset(&proc_info, 0, sizeof(proc_info));

    proc_t highest;
    long current_highest = 0;
    int current_score = -100;

    Core::DebugLog("Looking for a best candidate");

    while (true)
    {
        proc_info = readproc(proc, NULL);
        if (proc_info == NULL)
        {
            break;
        }
        if (!Configuration::KillRoot && (int)proc_info->euid == 0)
        {
            if (Configuration::Verbosity >= 6)
            {
                Core::DebugLog("Ignoring " + Core::int2String(proc_info->tid) + " owned by root", 6);
            }
            freeproc(proc_info);
            continue;
        }

        if (IgnoredId(proc_info->euid))
        {
            Core::DebugLog("Ignoring " + Core::int2String(proc_info->tid) + " owned by ignored account: " + Core::int2String(proc_info->euid), 2);
            freeproc(proc_info);
            continue;
        }

        if (proc_info->tid == Configuration::pid && !Configuration::KillSelf)
        {
            Core::DebugLog("Ignoring " + Core::int2String(proc_info->tid) + " which is current instance of this daemon", 6);
            freeproc(proc_info);
            continue;
        }

        int score = 0;

        // if it's a root process, decrease the score by 10

        if (proc_info->euser == 0)
        {
            score -= 10;
        }

        score = score + (int)proc_info->nice;

        int badness_score = Core::GetOom(proc_info->tid);

        if (badness_score <= -17)
        {
            // ignore process
            freeproc(proc_info);
            continue;
        }

        if (badness_score != 0)
        {
            score = score + badness_score;
        }

        // check if this process is using most memory
        if ( proc_info->resident * 4 > current_highest )
        {
            current_highest = proc_info->resident * 4;
            score += 10;
        }

        // if this process has highest score, we flag it for kill
        if ( score >= current_score )
        {
            highest = *proc_info;
            current_score = score;
        } else
        {
            if (Configuration::Verbosity >= 12)
            {
                Core::DebugLog("Process " + Name(proc_info) + "is eating less than highest candidate", 12);
            }
        }
        freeproc(proc_info);
    }

    if (current_score == -100)
    {
        Core::ErrorLog("Unable to find any process to kill. System is running OOM and I can't do anything to fix it.");
        closeproc(proc);
        return;
    }

    Core::Log("Most preferred process has score " + Core::int2String(current_score) + " : " + Name(&highest) +  " killing now");

    if (KillExec(&highest) == 0)
    {
        KillProc((pid_t)highest.tid, hard);
        Exec(&highest);
    }else
    {
        Core::Log("Not killed " + Name(&highest) + " because the test command returned different value");
    }

    closeproc(proc);
    return;
}
Example #6
0
int daemon_main(int argc, char *argv[])
{
    int opt;
    bool fork_flag = false;
    bool replace_flag = false;
    bool patch_sepolicy = true;

    enum {
        OPT_ALLOW_ROOT_CLIENT = 1000,
        OPT_NO_PATCH_SEPOLICY = 1001,
        OPT_SIGSTOP_WHEN_READY = 1002,
        OPT_LOG_TO_KMSG = 1003,
        OPT_LOG_TO_STDIO = 1004,
        OPT_NO_UNSHARE = 1005,
    };

    static struct option long_options[] = {
        {"daemonize",          no_argument, 0, 'd'},
        {"replace",            no_argument, 0, 'r'},
        {"help",               no_argument, 0, 'h'},
        {"allow-root-client",  no_argument, 0, OPT_ALLOW_ROOT_CLIENT},
        {"no-patch-sepolicy",  no_argument, 0, OPT_NO_PATCH_SEPOLICY},
        {"sigstop-when-ready", no_argument, 0, OPT_SIGSTOP_WHEN_READY},
        {"log-to-kmsg",        no_argument, 0, OPT_LOG_TO_KMSG},
        {"log-to-stdio",       no_argument, 0, OPT_LOG_TO_STDIO},
        {"no-unshare",         no_argument, 0, OPT_NO_UNSHARE},
        {0, 0, 0, 0}
    };

    int long_index = 0;

    while ((opt = getopt_long(argc, argv, "drh", long_options, &long_index)) != -1) {
        switch (opt) {
        case 'd':
            fork_flag = true;
            break;

        case 'r':
            replace_flag = true;
            break;

        case 'h':
            daemon_usage(0);
            return EXIT_SUCCESS;

        case OPT_ALLOW_ROOT_CLIENT:
            allow_root_client = true;
            break;

        case OPT_NO_PATCH_SEPOLICY:
            patch_sepolicy = false;
            break;

        case OPT_SIGSTOP_WHEN_READY:
            sigstop_when_ready = true;
            break;

        case OPT_LOG_TO_KMSG:
            log_to_kmsg = true;
            break;

        case OPT_LOG_TO_STDIO:
            log_to_stdio = true;
            break;

        case OPT_NO_UNSHARE:
            no_unshare = true;
            break;

        default:
            daemon_usage(1);
            return EXIT_FAILURE;
        }
    }

    // There should be no other arguments
    if (argc - optind != 0) {
        daemon_usage(1);
        return EXIT_FAILURE;
    }

    if (!no_unshare && unshare(CLONE_NEWNS) < 0) {
        fprintf(stderr, "unshare() failed: %s\n", strerror(errno));
        return EXIT_FAILURE;
    }

    if (patch_sepolicy) {
        patch_loaded_sepolicy(SELinuxPatch::MAIN);
    }

    if (!switch_context(MB_EXEC_CONTEXT)) {
        fprintf(stderr, "Failed to switch context; %s may not run properly",
                argv[0]);
    }

    if (replace_flag) {
        PROCTAB *proc = openproc(PROC_FILLCOM | PROC_FILLSTAT);
        if (proc) {
            pid_t curpid = getpid();

            while (proc_t *info = readproc(proc, nullptr)) {
                // NOTE: Can't check 'strcmp(info->cmd, "mbtool") == 0' (which
                // is the basename of /proc/<pid>/cmd) because the binary is not
                // always called "mbtool". For example, when run via SignedExec,
                // it's just called "binary".

                // If we can read the cmdline and argc >= 2
                if (info->cmdline && info->cmdline[0] && info->cmdline[1]) {
                    const char *name = strrchr(info->cmdline[0], '/');
                    if (name) {
                        ++name;
                    } else {
                        name = info->cmdline[0];
                    }

                    if (strcmp(name, "mbtool") == 0               // This is mbtool
                            && strstr(info->cmdline[1], "daemon") // And it's a daemon process
                            && info->tid != curpid) {             // And we're not killing ourself
                        // Kill the daemon process
                        LOGV("Killing PID %d", info->tid);
                        kill(info->tid, SIGTERM);
                    }
                }

                freeproc(info);
            }

            closeproc(proc);
        }

        // Give processes a chance to exit
        usleep(500000);
    }

    if (fork_flag) {
        run_daemon_fork();
    } else {
        return (daemon_init() && run_daemon())
                ? EXIT_SUCCESS : EXIT_FAILURE;
    }
}
Example #7
0
static void select_procs(void)
{
  static size_t size = 0;
  PROCTAB* ptp;
  proc_t* task;
  size_t match;
  char* cmd_arg0;
  char* cmd_arg0_base;
  char* cmd_arg1;
  char* cmd_arg1_base;
  char* program_base;
  char* root_link;
  char* exe_link;
  char* exe_link_base;
  
  program_base = basename(program);  /* get the input base name */
  ptp = openproc(PROC_FILLCOM | PROC_FILLSTAT);
  
  while ((task = readproc(ptp, NULL)))
    {
      if (epidof_root)
	{
	  /* get the /proc/<pid>/root symlink value */
	  root_link = pid_link(task->XXXID, "root");
	  match = !strcmp(epidof_root, root_link);
	  xfree(root_link);
	  
	  if (!match) /* root check failed */
	    {
	      freeproc(task);
	      continue;
	    }
	}
      
      if (!is_omitted(task->XXXID) && task->cmdline)
	{
	  cmd_arg0 = task->cmdline[0];
	  
	  /* processes starting with '-' are login shells */
	  if (*cmd_arg0 == '-')
	    cmd_arg0++;
	  
	  cmd_arg0_base = basename(cmd_arg0);           /* get the argv0 basename */
	  exe_link      = pid_link(task->XXXID, "exe");  /* get the /proc/<pid>/exe symlink value */
	  exe_link_base = basename(exe_link);           /* get the exe_link basename */
	  match = 0;
	  
#define __test(p, c)  (!strcmp(p, c##_base) || !strcmp(p, c) || !strcmp(p##_base, c))
	  
	  if (__test(program, cmd_arg0) || __test(program, exe_link))
	    match = 1;
	  else if (opt_scripts_too && task->cmdline[1])
	    {
	      cmd_arg1 = task->cmdline[1];
	      cmd_arg1_base = basename(cmd_arg1);
	      
	      /* if script, then task->cmd = argv1, otherwise task->cmd = argv0 */
	      if (task->cmd &&
		  !strncmp(task->cmd, cmd_arg1_base, strlen(task->cmd)) &&
		  __test(program, cmd_arg1))
		match = 1;
	    }
	  
#undef __test
	  
	  xfree(exe_link);
	  
	  if (match && environment_test(task->XXXID, *argv))
	    {
	      if (proc_count == size)
		procs = xrealloc(procs, __grow(size) * sizeof(*procs));
	      procs[proc_count++] = task->XXXID;
	    }
	}
      freeproc(task);
    }
  closeproc(ptp);
}
Example #8
0
int main(int argc, char ** argv) {
	char line[1024];
	char buf1[1024];
	char buf2[1024];
	int pid, newpid;
	char *sp1, *sp2;

	FILE *wlog = fopen("/dev/null", "w");
	FILE *rlog = fopen("/dev/null", "w");
	FILE *logfile;
	int opt;

	while ( (opt = getopt(argc, argv, "w:r:")) != -1 ) {
		switch (opt) {
		    case 'w':
			fclose(wlog);
			wlog = fopen(optarg, "w");
			break;

		    case 'r':
			fclose(rlog);
			rlog = fopen(optarg, "w");
			break;

		    default:
			fprintf(stderr, "Usage: %s [-w wlog-file] "
			                "[-r rlog-file]\n", argv[0]);
			return 1;
		}
	}

	if (fgets(line, 1024, stdin) &&
	    sscanf(line, "%d", &pid) == 1 ) {
		newproc(pid, getcwd((char *) NULL, 0) );
	} else {
		fprintf(stderr, "fl_stparse: Can't init using first line "
		                "of input!\n");
		return 1;
	}

	do {

		if ( sscanf(line, "%d fork() = %d", &pid, &newpid) == 2 ||
		     sscanf(line, "%d vfork() = %d", &pid, &newpid) == 2 ||
		     sscanf(line, "%d <... fork resumed> ) = %d",
							&pid, &newpid) == 2 ||
		     sscanf(line, "%d <... vfork resumed> ) = %d",
							&pid, &newpid) == 2) {
			sp1 = getproc(pid)->cwd;
			sp2 = malloc( strlen(sp1) + 1);
			strcpy(sp2, sp1);
			newproc(newpid, sp2 );
			continue;
		}

		if ( sscanf(line, "%d _exit(%d", &pid, &newpid) == 2 ||
		     sscanf(line, "%d exit_group(%d", &pid, &newpid) == 2 ) {
			freeproc(pid);
			continue;
		}

		if ( sscanf(line, "%d chdir(\"%[^\"]", &pid, buf1) == 2 ) {
			if (chdir(getproc(pid)->cwd) < 0 ||
			    chdir(buf1) < 0) {
				fprintf(stderr, "fl_stparse: Can't get proc %d's cwd! %s\n",
					pid, strerror(errno));
				exit(1);
			}
			setproc(pid, getcwd((char *) NULL, 0) );
			continue;
		}

		if ( sscanf(line, "%d open(\"%[^\"]\", %s",
						&pid, buf1, buf2) == 3 ) {

			if (strstr(buf2, "O_RDONLY") == NULL) logfile = wlog;
			else logfile = rlog;

			if (strstr(buf2, "O_DIRECTORY") != NULL) continue;

			if (buf1[0] == '/') {
				fprintf(logfile, "%d: %s\n", pid, buf1);
			} else {
				sp1 = getproc(pid)->cwd;
				if (!strcmp(sp1, "/")) sp1="";
				fprintf(logfile, "%d: %s/%s\n",
				                 pid, sp1, buf1);
			}
			continue;
		}

		if ( sscanf(line, "%d mkdir(\"%[^\"]\", ", &pid, buf1) == 2 ||
		     sscanf(line, "%d utime(\"%[^\"]\", ", &pid, buf1) == 2 ||
		     sscanf(line, "%d link(\"%[^\"]\", \"%[^\"]\"",
						&pid, buf2, buf1) == 3 ||
		     sscanf(line, "%d symlink(\"%[^\"]\", \"%[^\"]\"",
						&pid, buf2, buf1) == 3 ||
		     sscanf(line, "%d rename(\"%[^\"]\", \"%[^\"]\"",
						&pid, buf2, buf1) == 3 ) {
			if (buf1[0] == '/') {
				fprintf(wlog, "%d: %s\n", pid, buf1);
			} else {
				sp1 = getproc(pid)->cwd;
				if (!strcmp(sp1, "/")) sp1="";
				fprintf(wlog, "%d: %s/%s\n",
				              pid, sp1, buf1);
			}
			continue;
		}

	} while (fgets(line, 1024, stdin) != NULL);
	return 0;
}
Example #9
0
/*
 * Wait system call.
 * Search for a terminated (zombie) child,
 * finally lay it to rest, and collect its status.
 * Look also for stopped children,
 * and pass back status from them.
 */
int
waitid(idtype_t idtype, id_t id, k_siginfo_t *ip, int options)
{
	int found;
	proc_t *cp, *pp;
	int proc_gone;
	int waitflag = !(options & WNOWAIT);

	/*
	 * Obsolete flag, defined here only for binary compatibility
	 * with old statically linked executables.  Delete this when
	 * we no longer care about these old and broken applications.
	 */
#define	_WNOCHLD	0400
	options &= ~_WNOCHLD;

	if (options == 0 || (options & ~WOPTMASK))
		return (EINVAL);

	switch (idtype) {
	case P_PID:
	case P_PGID:
		if (id < 0 || id >= maxpid)
			return (EINVAL);
		/* FALLTHROUGH */
	case P_ALL:
		break;
	default:
		return (EINVAL);
	}

	pp = ttoproc(curthread);

	/*
	 * lock parent mutex so that sibling chain can be searched.
	 */
	mutex_enter(&pidlock);

	/*
	 * if we are only looking for exited processes and child_ns list
	 * is empty no reason to look at all children.
	 */
	if (idtype == P_ALL &&
	    (options & ~WNOWAIT) == (WNOHANG | WEXITED) &&
	    pp->p_child_ns == NULL) {
		if (pp->p_child) {
			mutex_exit(&pidlock);
			bzero(ip, sizeof (k_siginfo_t));
			return (0);
		}
		mutex_exit(&pidlock);
		return (ECHILD);
	}

	while (pp->p_child != NULL) {

		proc_gone = 0;

		for (cp = pp->p_child_ns; cp != NULL; cp = cp->p_sibling_ns) {
			if (idtype != P_PID && (cp->p_pidflag & CLDWAITPID))
				continue;
			if (idtype == P_PID && id != cp->p_pid)
				continue;
			if (idtype == P_PGID && id != cp->p_pgrp)
				continue;

			switch (cp->p_wcode) {

			case CLD_TRAPPED:
			case CLD_STOPPED:
			case CLD_CONTINUED:
				cmn_err(CE_PANIC,
				    "waitid: wrong state %d on the p_newstate"
				    " list", cp->p_wcode);
				break;

			case CLD_EXITED:
			case CLD_DUMPED:
			case CLD_KILLED:
				if (!(options & WEXITED)) {
					/*
					 * Count how many are already gone
					 * for good.
					 */
					proc_gone++;
					break;
				}
				if (!waitflag) {
					winfo(cp, ip, 0);
				} else {
					winfo(cp, ip, 1);
					freeproc(cp);
				}
				mutex_exit(&pidlock);
				if (waitflag) {		/* accept SIGCLD */
					sigcld_delete(ip);
					sigcld_repost();
				}
				return (0);
			}

			if (idtype == P_PID)
				break;
		}

		/*
		 * Wow! None of the threads on the p_sibling_ns list were
		 * interesting threads. Check all the kids!
		 */
		found = 0;
		for (cp = pp->p_child; cp != NULL; cp = cp->p_sibling) {
			if (idtype == P_PID && id != cp->p_pid)
				continue;
			if (idtype == P_PGID && id != cp->p_pgrp)
				continue;

			switch (cp->p_wcode) {
			case CLD_TRAPPED:
				if (!(options & WTRAPPED))
					break;
				winfo(cp, ip, waitflag);
				mutex_exit(&pidlock);
				if (waitflag) {		/* accept SIGCLD */
					sigcld_delete(ip);
					sigcld_repost();
				}
				return (0);

			case CLD_STOPPED:
				if (!(options & WSTOPPED))
					break;
				/* Is it still stopped? */
				mutex_enter(&cp->p_lock);
				if (!jobstopped(cp)) {
					mutex_exit(&cp->p_lock);
					break;
				}
				mutex_exit(&cp->p_lock);
				winfo(cp, ip, waitflag);
				mutex_exit(&pidlock);
				if (waitflag) {		/* accept SIGCLD */
					sigcld_delete(ip);
					sigcld_repost();
				}
				return (0);

			case CLD_CONTINUED:
				if (!(options & WCONTINUED))
					break;
				winfo(cp, ip, waitflag);
				mutex_exit(&pidlock);
				if (waitflag) {		/* accept SIGCLD */
					sigcld_delete(ip);
					sigcld_repost();
				}
				return (0);

			case CLD_EXITED:
			case CLD_DUMPED:
			case CLD_KILLED:
				if (idtype != P_PID &&
				    (cp->p_pidflag & CLDWAITPID))
					continue;
				/*
				 * Don't complain if a process was found in
				 * the first loop but we broke out of the loop
				 * because of the arguments passed to us.
				 */
				if (proc_gone == 0) {
					cmn_err(CE_PANIC,
					    "waitid: wrong state on the"
					    " p_child list");
				} else {
					break;
				}
			}

			found++;

			if (idtype == P_PID)
				break;
		}

		/*
		 * If we found no interesting processes at all,
		 * break out and return ECHILD.
		 */
		if (found + proc_gone == 0)
			break;

		if (options & WNOHANG) {
			mutex_exit(&pidlock);
			bzero(ip, sizeof (k_siginfo_t));
			/*
			 * We should set ip->si_signo = SIGCLD,
			 * but there is an SVVS test that expects
			 * ip->si_signo to be zero in this case.
			 */
			return (0);
		}

		/*
		 * If we found no processes of interest that could
		 * change state while we wait, we don't wait at all.
		 * Get out with ECHILD according to SVID.
		 */
		if (found == proc_gone)
			break;

		if (!cv_wait_sig_swap(&pp->p_cv, &pidlock)) {
			mutex_exit(&pidlock);
			return (EINTR);
		}
	}
	mutex_exit(&pidlock);
	return (ECHILD);
}
Example #10
0
/*
 * Return value:
 *   1 - exitlwps() failed, call (or continue) lwp_exit()
 *   0 - restarting init.  Return through system call path
 */
int
proc_exit(int why, int what)
{
	kthread_t *t = curthread;
	klwp_t *lwp = ttolwp(t);
	proc_t *p = ttoproc(t);
	zone_t *z = p->p_zone;
	timeout_id_t tmp_id;
	int rv;
	proc_t *q;
	task_t *tk;
	vnode_t *exec_vp, *execdir_vp, *cdir, *rdir;
	sigqueue_t *sqp;
	lwpdir_t *lwpdir;
	uint_t lwpdir_sz;
	tidhash_t *tidhash;
	uint_t tidhash_sz;
	ret_tidhash_t *ret_tidhash;
	refstr_t *cwd;
	hrtime_t hrutime, hrstime;
	int evaporate;

	/*
	 * Stop and discard the process's lwps except for the current one,
	 * unless some other lwp beat us to it.  If exitlwps() fails then
	 * return and the calling lwp will call (or continue in) lwp_exit().
	 */
	proc_is_exiting(p);
	if (exitlwps(0) != 0)
		return (1);

	mutex_enter(&p->p_lock);
	if (p->p_ttime > 0) {
		/*
		 * Account any remaining ticks charged to this process
		 * on its way out.
		 */
		(void) task_cpu_time_incr(p->p_task, p->p_ttime);
		p->p_ttime = 0;
	}
	mutex_exit(&p->p_lock);

	DTRACE_PROC(lwp__exit);
	DTRACE_PROC1(exit, int, why);

	/*
	 * Will perform any brand specific proc exit processing, since this
	 * is always the last lwp, will also perform lwp_exit and free brand
	 * data
	 */
	if (PROC_IS_BRANDED(p)) {
		lwp_detach_brand_hdlrs(lwp);
		brand_clearbrand(p, B_FALSE);
	}

	/*
	 * Don't let init exit unless zone_start_init() failed its exec, or
	 * we are shutting down the zone or the machine.
	 *
	 * Since we are single threaded, we don't need to lock the
	 * following accesses to zone_proc_initpid.
	 */
	if (p->p_pid == z->zone_proc_initpid) {
		if (z->zone_boot_err == 0 &&
		    zone_status_get(z) < ZONE_IS_SHUTTING_DOWN &&
		    zone_status_get(global_zone) < ZONE_IS_SHUTTING_DOWN &&
		    z->zone_restart_init == B_TRUE &&
		    restart_init(what, why) == 0)
			return (0);
		/*
		 * Since we didn't or couldn't restart init, we clear
		 * the zone's init state and proceed with exit
		 * processing.
		 */
		z->zone_proc_initpid = -1;
	}

	lwp_pcb_exit();

	/*
	 * Allocate a sigqueue now, before we grab locks.
	 * It will be given to sigcld(), below.
	 * Special case:  If we will be making the process disappear
	 * without a trace because it is either:
	 *	* an exiting SSYS process, or
	 *	* a posix_spawn() vfork child who requests it,
	 * we don't bother to allocate a useless sigqueue.
	 */
	evaporate = (p->p_flag & SSYS) || ((p->p_flag & SVFORK) &&
	    why == CLD_EXITED && what == _EVAPORATE);
	if (!evaporate)
		sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);

	/*
	 * revoke any doors created by the process.
	 */
	if (p->p_door_list)
		door_exit();

	/*
	 * Release schedctl data structures.
	 */
	if (p->p_pagep)
		schedctl_proc_cleanup();

	/*
	 * make sure all pending kaio has completed.
	 */
	if (p->p_aio)
		aio_cleanup_exit();

	/*
	 * discard the lwpchan cache.
	 */
	if (p->p_lcp != NULL)
		lwpchan_destroy_cache(0);

	/*
	 * Clean up any DTrace helper actions or probes for the process.
	 */
	if (p->p_dtrace_helpers != NULL) {
		ASSERT(dtrace_helpers_cleanup != NULL);
		(*dtrace_helpers_cleanup)();
	}

	/* untimeout the realtime timers */
	if (p->p_itimer != NULL)
		timer_exit();

	if ((tmp_id = p->p_alarmid) != 0) {
		p->p_alarmid = 0;
		(void) untimeout(tmp_id);
	}

	/*
	 * Remove any fpollinfo_t's for this (last) thread from our file
	 * descriptors so closeall() can ASSERT() that they're all gone.
	 */
	pollcleanup();

	if (p->p_rprof_cyclic != CYCLIC_NONE) {
		mutex_enter(&cpu_lock);
		cyclic_remove(p->p_rprof_cyclic);
		mutex_exit(&cpu_lock);
	}

	mutex_enter(&p->p_lock);

	/*
	 * Clean up any DTrace probes associated with this process.
	 */
	if (p->p_dtrace_probes) {
		ASSERT(dtrace_fasttrap_exit_ptr != NULL);
		dtrace_fasttrap_exit_ptr(p);
	}

	while ((tmp_id = p->p_itimerid) != 0) {
		p->p_itimerid = 0;
		mutex_exit(&p->p_lock);
		(void) untimeout(tmp_id);
		mutex_enter(&p->p_lock);
	}

	lwp_cleanup();

	/*
	 * We are about to exit; prevent our resource associations from
	 * being changed.
	 */
	pool_barrier_enter();

	/*
	 * Block the process against /proc now that we have really
	 * acquired p->p_lock (to manipulate p_tlist at least).
	 */
	prbarrier(p);

	sigfillset(&p->p_ignore);
	sigemptyset(&p->p_siginfo);
	sigemptyset(&p->p_sig);
	sigemptyset(&p->p_extsig);
	sigemptyset(&t->t_sig);
	sigemptyset(&t->t_extsig);
	sigemptyset(&p->p_sigmask);
	sigdelq(p, t, 0);
	lwp->lwp_cursig = 0;
	lwp->lwp_extsig = 0;
	p->p_flag &= ~(SKILLED | SEXTKILLED);
	if (lwp->lwp_curinfo) {
		siginfofree(lwp->lwp_curinfo);
		lwp->lwp_curinfo = NULL;
	}

	t->t_proc_flag |= TP_LWPEXIT;
	ASSERT(p->p_lwpcnt == 1 && p->p_zombcnt == 0);
	prlwpexit(t);		/* notify /proc */
	lwp_hash_out(p, t->t_tid);
	prexit(p);

	p->p_lwpcnt = 0;
	p->p_tlist = NULL;
	sigqfree(p);
	term_mstate(t);
	p->p_mterm = gethrtime();

	exec_vp = p->p_exec;
	execdir_vp = p->p_execdir;
	p->p_exec = NULLVP;
	p->p_execdir = NULLVP;
	mutex_exit(&p->p_lock);

	pr_free_watched_pages(p);

	closeall(P_FINFO(p));

	/* Free the controlling tty.  (freectty() always assumes curproc.) */
	ASSERT(p == curproc);
	(void) freectty(B_TRUE);

#if defined(__sparc)
	if (p->p_utraps != NULL)
		utrap_free(p);
#endif
	if (p->p_semacct)			/* IPC semaphore exit */
		semexit(p);
	rv = wstat(why, what);

	acct(rv & 0xff);
	exacct_commit_proc(p, rv);

	/*
	 * Release any resources associated with C2 auditing
	 */
	if (AU_AUDITING()) {
		/*
		 * audit exit system call
		 */
		audit_exit(why, what);
	}

	/*
	 * Free address space.
	 */
	relvm();

	if (exec_vp) {
		/*
		 * Close this executable which has been opened when the process
		 * was created by getproc().
		 */
		(void) VOP_CLOSE(exec_vp, FREAD, 1, (offset_t)0, CRED(), NULL);
		VN_RELE(exec_vp);
	}
	if (execdir_vp)
		VN_RELE(execdir_vp);

	/*
	 * Release held contracts.
	 */
	contract_exit(p);

	/*
	 * Depart our encapsulating process contract.
	 */
	if ((p->p_flag & SSYS) == 0) {
		ASSERT(p->p_ct_process);
		contract_process_exit(p->p_ct_process, p, rv);
	}

	/*
	 * Remove pool association, and block if requested by pool_do_bind.
	 */
	mutex_enter(&p->p_lock);
	ASSERT(p->p_pool->pool_ref > 0);
	atomic_add_32(&p->p_pool->pool_ref, -1);
	p->p_pool = pool_default;
	/*
	 * Now that our address space has been freed and all other threads
	 * in this process have exited, set the PEXITED pool flag.  This
	 * tells the pools subsystems to ignore this process if it was
	 * requested to rebind this process to a new pool.
	 */
	p->p_poolflag |= PEXITED;
	pool_barrier_exit();
	mutex_exit(&p->p_lock);

	mutex_enter(&pidlock);

	/*
	 * Delete this process from the newstate list of its parent. We
	 * will put it in the right place in the sigcld in the end.
	 */
	delete_ns(p->p_parent, p);

	/*
	 * Reassign the orphans to the next of kin.
	 * Don't rearrange init's orphanage.
	 */
	if ((q = p->p_orphan) != NULL && p != proc_init) {

		proc_t *nokp = p->p_nextofkin;

		for (;;) {
			q->p_nextofkin = nokp;
			if (q->p_nextorph == NULL)
				break;
			q = q->p_nextorph;
		}
		q->p_nextorph = nokp->p_orphan;
		nokp->p_orphan = p->p_orphan;
		p->p_orphan = NULL;
	}

	/*
	 * Reassign the children to init.
	 * Don't try to assign init's children to init.
	 */
	if ((q = p->p_child) != NULL && p != proc_init) {
		struct proc	*np;
		struct proc	*initp = proc_init;
		boolean_t	setzonetop = B_FALSE;

		if (!INGLOBALZONE(curproc))
			setzonetop = B_TRUE;

		pgdetach(p);

		do {
			np = q->p_sibling;
			/*
			 * Delete it from its current parent new state
			 * list and add it to init new state list
			 */
			delete_ns(q->p_parent, q);

			q->p_ppid = 1;
			q->p_pidflag &= ~(CLDNOSIGCHLD | CLDWAITPID);
			if (setzonetop) {
				mutex_enter(&q->p_lock);
				q->p_flag |= SZONETOP;
				mutex_exit(&q->p_lock);
			}
			q->p_parent = initp;

			/*
			 * Since q will be the first child,
			 * it will not have a previous sibling.
			 */
			q->p_psibling = NULL;
			if (initp->p_child) {
				initp->p_child->p_psibling = q;
			}
			q->p_sibling = initp->p_child;
			initp->p_child = q;
			if (q->p_proc_flag & P_PR_PTRACE) {
				mutex_enter(&q->p_lock);
				sigtoproc(q, NULL, SIGKILL);
				mutex_exit(&q->p_lock);
			}
			/*
			 * sigcld() will add the child to parents
			 * newstate list.
			 */
			if (q->p_stat == SZOMB)
				sigcld(q, NULL);
		} while ((q = np) != NULL);

		p->p_child = NULL;
		ASSERT(p->p_child_ns == NULL);
	}

	TRACE_1(TR_FAC_PROC, TR_PROC_EXIT, "proc_exit: %p", p);

	mutex_enter(&p->p_lock);
	CL_EXIT(curthread); /* tell the scheduler that curthread is exiting */

	/*
	 * Have our task accummulate our resource usage data before they
	 * become contaminated by p_cacct etc., and before we renounce
	 * membership of the task.
	 *
	 * We do this regardless of whether or not task accounting is active.
	 * This is to avoid having nonsense data reported for this task if
	 * task accounting is subsequently enabled. The overhead is minimal;
	 * by this point, this process has accounted for the usage of all its
	 * LWPs. We nonetheless do the work here, and under the protection of
	 * pidlock, so that the movement of the process's usage to the task
	 * happens at the same time as the removal of the process from the
	 * task, from the point of view of exacct_snapshot_task_usage().
	 */
	exacct_update_task_mstate(p);

	hrutime = mstate_aggr_state(p, LMS_USER);
	hrstime = mstate_aggr_state(p, LMS_SYSTEM);
	p->p_utime = (clock_t)NSEC_TO_TICK(hrutime) + p->p_cutime;
	p->p_stime = (clock_t)NSEC_TO_TICK(hrstime) + p->p_cstime;

	p->p_acct[LMS_USER]	+= p->p_cacct[LMS_USER];
	p->p_acct[LMS_SYSTEM]	+= p->p_cacct[LMS_SYSTEM];
	p->p_acct[LMS_TRAP]	+= p->p_cacct[LMS_TRAP];
	p->p_acct[LMS_TFAULT]	+= p->p_cacct[LMS_TFAULT];
	p->p_acct[LMS_DFAULT]	+= p->p_cacct[LMS_DFAULT];
	p->p_acct[LMS_KFAULT]	+= p->p_cacct[LMS_KFAULT];
	p->p_acct[LMS_USER_LOCK] += p->p_cacct[LMS_USER_LOCK];
	p->p_acct[LMS_SLEEP]	+= p->p_cacct[LMS_SLEEP];
	p->p_acct[LMS_WAIT_CPU]	+= p->p_cacct[LMS_WAIT_CPU];
	p->p_acct[LMS_STOPPED]	+= p->p_cacct[LMS_STOPPED];

	p->p_ru.minflt	+= p->p_cru.minflt;
	p->p_ru.majflt	+= p->p_cru.majflt;
	p->p_ru.nswap	+= p->p_cru.nswap;
	p->p_ru.inblock	+= p->p_cru.inblock;
	p->p_ru.oublock	+= p->p_cru.oublock;
	p->p_ru.msgsnd	+= p->p_cru.msgsnd;
	p->p_ru.msgrcv	+= p->p_cru.msgrcv;
	p->p_ru.nsignals += p->p_cru.nsignals;
	p->p_ru.nvcsw	+= p->p_cru.nvcsw;
	p->p_ru.nivcsw	+= p->p_cru.nivcsw;
	p->p_ru.sysc	+= p->p_cru.sysc;
	p->p_ru.ioch	+= p->p_cru.ioch;

	p->p_stat = SZOMB;
	p->p_proc_flag &= ~P_PR_PTRACE;
	p->p_wdata = what;
	p->p_wcode = (char)why;

	cdir = PTOU(p)->u_cdir;
	rdir = PTOU(p)->u_rdir;
	cwd = PTOU(p)->u_cwd;

	ASSERT(cdir != NULL || p->p_parent == &p0);

	/*
	 * Release resource controls, as they are no longer enforceable.
	 */
	rctl_set_free(p->p_rctls);

	/*
	 * Decrement tk_nlwps counter for our task.max-lwps resource control.
	 * An extended accounting record, if that facility is active, is
	 * scheduled to be written.  We cannot give up task and project
	 * membership at this point because that would allow zombies to escape
	 * from the max-processes resource controls.  Zombies stay in their
	 * current task and project until the process table slot is released
	 * in freeproc().
	 */
	tk = p->p_task;

	mutex_enter(&p->p_zone->zone_nlwps_lock);
	tk->tk_nlwps--;
	tk->tk_proj->kpj_nlwps--;
	p->p_zone->zone_nlwps--;
	mutex_exit(&p->p_zone->zone_nlwps_lock);

	/*
	 * Clear the lwp directory and the lwpid hash table
	 * now that /proc can't bother us any more.
	 * We free the memory below, after dropping p->p_lock.
	 */
	lwpdir = p->p_lwpdir;
	lwpdir_sz = p->p_lwpdir_sz;
	tidhash = p->p_tidhash;
	tidhash_sz = p->p_tidhash_sz;
	ret_tidhash = p->p_ret_tidhash;
	p->p_lwpdir = NULL;
	p->p_lwpfree = NULL;
	p->p_lwpdir_sz = 0;
	p->p_tidhash = NULL;
	p->p_tidhash_sz = 0;
	p->p_ret_tidhash = NULL;

	/*
	 * If the process has context ops installed, call the exit routine
	 * on behalf of this last remaining thread. Normally exitpctx() is
	 * called during thread_exit() or lwp_exit(), but because this is the
	 * last thread in the process, we must call it here. By the time
	 * thread_exit() is called (below), the association with the relevant
	 * process has been lost.
	 *
	 * We also free the context here.
	 */
	if (p->p_pctx) {
		kpreempt_disable();
		exitpctx(p);
		kpreempt_enable();

		freepctx(p, 0);
	}

	/*
	 * curthread's proc pointer is changed to point to the 'sched'
	 * process for the corresponding zone, except in the case when
	 * the exiting process is in fact a zsched instance, in which
	 * case the proc pointer is set to p0.  We do so, so that the
	 * process still points at the right zone when we call the VN_RELE()
	 * below.
	 *
	 * This is because curthread's original proc pointer can be freed as
	 * soon as the child sends a SIGCLD to its parent.  We use zsched so
	 * that for user processes, even in the final moments of death, the
	 * process is still associated with its zone.
	 */
	if (p != t->t_procp->p_zone->zone_zsched)
		t->t_procp = t->t_procp->p_zone->zone_zsched;
	else
		t->t_procp = &p0;

	mutex_exit(&p->p_lock);
	if (!evaporate) {
		p->p_pidflag &= ~CLDPEND;
		sigcld(p, sqp);
	} else {
		/*
		 * Do what sigcld() would do if the disposition
		 * of the SIGCHLD signal were set to be ignored.
		 */
		cv_broadcast(&p->p_srwchan_cv);
		freeproc(p);
	}
	mutex_exit(&pidlock);

	/*
	 * We don't release u_cdir and u_rdir until SZOMB is set.
	 * This protects us against dofusers().
	 */
	if (cdir)
		VN_RELE(cdir);
	if (rdir)
		VN_RELE(rdir);
	if (cwd)
		refstr_rele(cwd);

	/*
	 * task_rele() may ultimately cause the zone to go away (or
	 * may cause the last user process in a zone to go away, which
	 * signals zsched to go away).  So prior to this call, we must
	 * no longer point at zsched.
	 */
	t->t_procp = &p0;

	kmem_free(lwpdir, lwpdir_sz * sizeof (lwpdir_t));
	kmem_free(tidhash, tidhash_sz * sizeof (tidhash_t));
	while (ret_tidhash != NULL) {
		ret_tidhash_t *next = ret_tidhash->rth_next;
		kmem_free(ret_tidhash->rth_tidhash,
		    ret_tidhash->rth_tidhash_sz * sizeof (tidhash_t));
		kmem_free(ret_tidhash, sizeof (*ret_tidhash));
		ret_tidhash = next;
	}

	thread_exit();
	/* NOTREACHED */
}
Example #11
0
/*
 *	Read the proc filesystem.
 */
static int readproc(void)
{
	DIR *dir;
	struct dirent *d;
	char path[256];
	char buf[256];
	char *s, *q;
	FILE *fp;
	int pid, f;
	PROC *p;
	struct stat st;
	int c;

	/* Open the /proc directory. */
	if ((dir = opendir("/proc")) == NULL) {
		BTERROR("cannot opendir(/proc)");
		return -1;
	}

	freeproc();

	/* Walk through the directory. */
	while ((d = readdir(dir)) != NULL) {
		/* See if this is a process */
		if ((pid = atoi(d->d_name)) == 0) continue;
		/* Get a PROC struct . */
		p = (PROC *)malloc(sizeof(PROC));
		memset(p, 0, sizeof(PROC));
		/* Open the status file. */
		snprintf(path, sizeof(path), "/proc/%s/stat", d->d_name);

		/* Read SID & statname from it. */
		if ((fp = fopen(path, "r")) != NULL) {
			buf[0] = 0;
			fgets(buf, 256, fp);

			/* See if name starts with '(' */
			s = buf;
			while (*s != ' ') s++;
			s++;
			if (*s == '(') {
				/* Read program name. */
				q = strrchr(buf, ')');
				if (q == NULL) {
					p->sid = 0;
					BTERROR("can't get program name from %s\n", path);
					free(p);
					continue;
				}
				s++;
			} else {
				q = s;
				while (*q != ' ') q++;
			}
			*q++ = 0;
			while (*q == ' ') q++;
			p->statname = strdup(s);

			/* This could be replaced by getsid(pid) */
			if (sscanf(q, "%*c %*d %*d %d", &p->sid) != 1) {
				p->sid = 0;
				BTERROR("can't read sid from %s\n",
						path);
				free(p);
				continue;
			}
			fclose(fp);
		} else {
			/* Process disappeared.. */
			free(p);
			continue;
		}

		/* Now read argv[0] */
		snprintf(path, sizeof(path), "/proc/%s/cmdline", d->d_name);
		if ((fp = fopen(path, "r")) != NULL) {
			f = 0;
			while(f < 127 && (c = fgetc(fp)) != EOF && c)
				buf[f++] = c;
			buf[f++] = 0;
			fclose(fp);

			/* Store the name into malloced memory. */
			p->fullname = strdup(buf);

			/* Get a pointer to the basename. */
			if ((p->basename = strrchr(p->fullname, '/')) != NULL)
				p->basename++;
			else
				p->basename = p->fullname;
		} else {
			/* Process disappeared.. */
			free(p);
			continue;
		}

		/* Try to stat the executable. */
		snprintf(path, sizeof(path), "/proc/%s/exe", d->d_name);
		if (stat(path, &st) == 0) {
			p->dev = st.st_dev;
			p->ino = st.st_ino;
		}

		/* Link it into the list. */
		p->next = plist;
		plist = p;
		p->pid = pid;
	}
	closedir(dir);
	/* Done. */
	return 0;
}
Example #12
0
int daemon_main(int argc, char *argv[])
{
    int opt;
    bool fork_flag = false;
    bool replace_flag = false;

    static struct option long_options[] = {
        {"daemonize", no_argument, 0, 'd'},
        {"replace",   no_argument, 0, 'r'},
        {"help",      no_argument, 0, 'h'},
        {0, 0, 0, 0}
    };

    int long_index = 0;

    while ((opt = getopt_long(argc, argv, "drh", long_options, &long_index)) != -1) {
        switch (opt) {
        case 'd':
            fork_flag = true;
            break;

        case 'r':
            replace_flag = true;
            break;

        case 'h':
            daemon_usage(0);
            return EXIT_SUCCESS;

        default:
            daemon_usage(1);
            return EXIT_FAILURE;
        }
    }

    // There should be no other arguments
    if (argc - optind != 0) {
        daemon_usage(1);
        return EXIT_FAILURE;
    }

    // Patch SELinux policy to make init permissive
    patch_loaded_sepolicy();

    // Allow untrusted_app to connect to our daemon
    patch_sepolicy_daemon();

    // Set version property if we're the system mbtool (i.e. launched by init)
    // Possible to override this with another program by double forking, letting
    // 2nd child reparent to init, and then calling execve("/mbtool", ...), but
    // meh ...
    if (getppid() == 1) {
        if (!util::set_property("ro.multiboot.version", get_mbtool_version())) {
            std::printf("Failed to set 'ro.multiboot.version' to '%s'\n",
                        get_mbtool_version());
        }
    }

    if (replace_flag) {
        PROCTAB *proc = openproc(PROC_FILLCOM | PROC_FILLSTAT);
        if (proc) {
            pid_t curpid = getpid();

            while (proc_t *info = readproc(proc, nullptr)) {
                if (strcmp(info->cmd, "mbtool") == 0          // This is mbtool
                        && info->cmdline                      // And we can see the command line
                        && info->cmdline[1]                   // And argc > 1
                        && strstr(info->cmdline[1], "daemon") // And it's a daemon process
                        && info->tid != curpid) {             // And we're not killing ourself
                    // Kill the daemon process
                    std::printf("Killing PID %d\n", info->tid);
                    kill(info->tid, SIGTERM);
                }

                freeproc(info);
            }

            closeproc(proc);
        }

        // Give processes a chance to exit
        usleep(500000);
    }

    // Set up logging
    if (!util::mkdir_parent(MULTIBOOT_LOG_DAEMON, 0775) && errno != EEXIST) {
        fprintf(stderr, "Failed to create parent directory of %s: %s\n",
                MULTIBOOT_LOG_DAEMON, strerror(errno));
        return EXIT_FAILURE;
    }

    autoclose::file fp(autoclose::fopen(MULTIBOOT_LOG_DAEMON, "w"));
    if (!fp) {
        fprintf(stderr, "Failed to open log file %s: %s\n",
                MULTIBOOT_LOG_DAEMON, strerror(errno));
        return EXIT_FAILURE;
    }

    fix_multiboot_permissions();

    // mbtool logging
    log::log_set_logger(std::make_shared<log::StdioLogger>(fp.get(), true));

    if (fork_flag) {
        run_daemon_fork();
    } else {
        return run_daemon() ? EXIT_SUCCESS : EXIT_FAILURE;
    }
}