views:

230

answers:

2

I have a massive number of shell commands being executed with root/admin priveleges through Authorization Services' "AuthorizationExecuteWithPrivileges" call. The issue is that after a while (10-15 seconds, maybe 100 shell commands) the program stops responding with this error in the debugger:

couldn't fork: errno 35

And then while the app is running, I cannot launch any more applications. I researched this issue and apparently it means that there are no more threads available for the system to use. However, I checked using Activity Monitor and my app is only using 4-5 threads.

To fix this problem, I think what I need to do is separate the shell commands into a separate thread (away from the main thread). I have never used threading before, and I'm unsure where to start (no comprehensive examples I could find)

Thanks

+1  A: 

This is not about threads (at least not threads in your application). This is about system resources. Each of those forked processes is consuming at least 1 kernel thread (maybe more), some vnodes, and a number of other things. Eventually the system will not allow you to spawn more processes.

The first limits you hit are administrative limits. The system can support more, but it may causes degraded performance and other issues. You can usually raise these through various mecahanisms, like sysctls. In general doing that is a bad idea unless you have a particular (special) work load that you know will benefit from specific tweaks.

Chances are raising those limits will not fix your issues. While adjusting those limits may make you run a little longer, in order to actually fix it you need to figure out why the resources are not being returned to the system. Based on what you described above I would guess that your forked processes are never exiting.

Louis Gerbarg
IIRC, you also have to ensure that someone is calling `wait(2)` as well. The `init` process will wait on them eventually, but you can run out of resources in the meantime if the runtime of each child process is very short.
D.Shawley
This is true, in general you need to call wait() or witpid() unless the processes somehow detach themselves.
Louis Gerbarg
hmm can you elaborate on how to use wait() in this situation?Thanks
macatomy
You just call wait() (in the parent) and the binary waits there until a child finishes, then wait returns. You should call wait once for each fork, though you can do several forks and then several waits if you want. You can also use waitpid() to wait for a specific child.
Louis Gerbarg
+3  A: 

As Louis Gerbarg already pointed out, your question has nothing to do with threads. I've edited your title and tags accordingly.

I have a massive number of shell commands being executed with root/admin priveleges through Authorization Services' "AuthorizationExecuteWithPrivileges" call.

Don't do that. That function only exists so you can restore the root:admin ownership and the setuid mode bit to the tool that you want to run as root.

The idea is that you should factor out the code that should run as root into a completely separate program from the part that does not need to run as root, so that the part that needs root can have it (through the setuid bit) and the part that doesn't need root can go without it (through not having setuid).

A code example is in the Authorization Services Programming Guide.

The issue is that after a while (10-15 seconds, maybe 100 shell commands) the program stops responding with this error in the debugger:

couldn't fork: errno 35

Yeah. You can only run a couple hundred processes at a time. This is an OS-enforced limit.

It's a soft limit, which means you can raise it—but only up to the hard limit, which you cannot raise. See the output of limit and limit -h (in zsh; I don't know about other shells).

You need to wait for processes to finish before running more processes.

And then while the app is running, I cannot launch any more applications.

Because you are already running as many processes as you're allowed to. That x-hundred-process limit is per-user, not per-process.

I researched this issue and apparently it means that there are no more threads available for the system to use.

No, it does not.

The errno error codes are used for many things. EAGAIN (35, “resource temporarily unavailable”) may mean no more threads when set by a system call that starts a thread, but it does not mean that when set by another system call or function.

The error message you quoted explicitly says that it was set by fork, which is the system call to start a new process, not a new thread. In that context, EAGAIN means “you are already running as many processes as you can”. See the fork manpage.

However, I checked using Activity Monitor and my app is only using 4-5 threads.

See?

To fix this problem, I think what I need to do is separate the shell commands into a separate thread (away from the main thread).

Starting one process per thread will only help you run out of processes much faster.

I have never used threading before …

It sounds like you still haven't, since the function you're referring to starts a process, not a thread.

Peter Hosey
Thanks for the answers :) I think I should create a new CLI tool or script that runs these commands and then have the app execute the tool with admin priveleges. Would that work?
macatomy
That's only a good solution as long as you don't make it able to run *any* process as root. That would be a tremendous security hole. Either make one tool per task or, if your tool runs another tool installed on the system, make your tool only accept choices that are in a white-list.
Peter Hosey
Furthermore, if your tool does run a tool that is installed on the system, make sure you restrict PATH to only the places where it could be. Hard-coded paths (e.g., /bin/ls) may be even better. Don't allow an attacker to use PATH to run code of their choice under an assumed name.
Peter Hosey
Yeah I'll use hard coded paths. Also, another thing. I'm using a progress bar to indicate how many files have been copied. With a separate tool, is there any way I could still see how many files from the list have been copied (as in like a live feed) so that I could update my progress bar accordingly? I suppose I could have it log numbers like "1" and "2" to a log file and then have the app check the log at timed intervals to see what has been copied, but is there a better way?Thanks
macatomy
That's not relevant to this question. You should ask it as a new question.
Peter Hosey
In bash `ulimit` tells you about configurable resource limits. Use `ulimit -a` for the big picture.
dmckee