Want to highlight a helpful answer? Upvote!

Did someone help you, or did an answer or User Tip resolve your issue? Upvote by selecting the upvote arrow. Your feedback helps others! Learn more about when to upvote >

Looks like no one’s replied in a while. To start the conversation again, simply ask a new question.

Is there a launch daemon size limit?

On several occasions I have put bash scripts into launch daemons with relatively few difficulties but I have met a strange problem.


I use the following structure:


<key>ProgramArguments</key>

<array>

<string>/bin/bash</string>

<string>-c</string>

<string>bash script</string>


On this occasion it is located at: /Library/LaunchAgents.


I have resolved all the bugs but the script gets confused when its size exceeds about 1,100 bytes - XML plist size about 1500 bytes.


What limit am I exceeding on my OS X 10.5.8 and how can I increase it?


I am aware that it may be better to run such a large script from another location but I would like to know the answers to my question.


My default limits are:


$ ulimit -a

core file size (blocks, -c) 0

data seg size (kbytes, -d) unlimited

file size (blocks, -f) unlimited

max locked memory (kbytes, -l) unlimited

max memory size (kbytes, -m) unlimited

open files (-n) 256

pipe size (512 bytes, -p) 1

stack size (kbytes, -s) 8192

cpu time (seconds, -t) unlimited

max user processes (-u) 266

virtual memory (kbytes, -v) unlimited

Posted on Jul 3, 2014 1:09 PM

Reply
45 replies

Jul 3, 2014 1:37 PM in response to Neville Hillyer

I suggest you don't do that. launchd is designed to be 'light'; it's so central to the system that they don't want it getting bogged down, so they've geared it to being simple.. If you have a long bash script to run, write it as a separate script and call it from the plist file by writing its address into the Program or ProgramArguments key.


I understand the desire to keep things together in one place, but if you try for a workaround to achieve that relatively minor goal in this particular case you may find it has unfortunate unintended consequences on the entire OS.

Jul 3, 2014 11:28 PM in response to Neville Hillyer

You could try looking at the man pages for launched.plist and launchctl, specifically the HardResourceLimits key in the former and the limit subcommand in the latter. launched will use those to set resource limits on particular jobs, though I can only guess at which limits you might be exceeding. That may work if the issue you're having is a per-job limitation; it won't help at all if it's a system-wide limitation.


And don't get me wrong: putting the bash script in a separate file is the sensible thing to do so I'm obliged to point it out, but I know that many computer-oriented people have an aversion to being sensible. Coders have an artistic temperament that sometimes brooks no opposition. You're good enough at this stuff so that if you torch your system - a distinct possibility if you muck around with launched the wrong way - you can reinstall it without too much trouble. Just so long as you know it's a possibility.

Jul 4, 2014 9:38 AM in response to Neville Hillyer

@ etresoft


I apologise for not being more specific. I hope the details below will clarify the situation a little.


@ twtwtw


Thanks for your details. They have improved my understanding but I have not yet found where my unusual limit is defined. Please see below.


Since your posts I have been playing with the following test plist:


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">

<dict>

<key>EnableGlobbing</key>

<true/>

<key>Label</key>

<string>reset-safari</string>

<key>ProgramArguments</key>

<array>

<string>/bin/bash</string>

<string>-c</string>

<string>echo "

123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789

23456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789

3456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1

56789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123

6789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234

789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345

89 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456

9 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234567

123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345678

123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12

" &gt; /Users/neville/Desktop/tmp.txt

</string>

</array>

<key>RunAtLoad</key>

<true/>

</dict>

</plist>


I have discovered that with EnableGlobbing set to true the maximum size of the bash script which will work is 1024 bytes. As the size is increased further the name of tmp.txt starts getting truncated and eventually random errors are generated such as - unexpected EOF while looking for matching `"' - I have had errors claiming a missing quote or bracket even when they do not exist in my script.


With EnableGlobbing set to false the maximum size of the bash script which will work is 4096 bytes. As the size is increased further output is totally inhibited without file truncation or any error reports - random or otherwise.


I have noted that any bash comments count towards the above maxima.


I would be grateful if anybody can shed further light on this.


I will investigate alternatives to EnableGlobbing - I have not used it before and only need it this time to expand '~' in one location.

Jul 4, 2014 10:55 AM in response to Neville Hillyer

Neville Hillyer wrote:


I have discovered that with EnableGlobbing set to true the maximum size of the bash script which will work is 1024 bytes. As the size is increased further the name of tmp.txt starts getting truncated and eventually random errors are generated such as - unexpected EOF while looking for matching `"' - I have had errors claiming a missing quote or bracket even when they do not exist in my script.


With EnableGlobbing set to false the maximum size of the bash script which will work is 4096 bytes. As the size is increased further output is totally inhibited without file truncation or any error reports - random or otherwise.


I have noted that any bash comments count towards the above maxima.


I would be grateful if anybody can shed further light on this.


I will investigate alternatives to EnableGlobbing - I have not used it before and only need it this time to expand '~' in one location.


This points to what I was trying to talk about earlier; launchd is a stripped-down process. Maybe a quick explanation of how launchd works will help.


Launchd has three elements:


  • The launchd daemon: this is process 0, the root process that starts up the system and directly or indirectly launches and governs all other processes. There are actually separate launchd processes for each user which are launched by the systemwide launchd, but never mind that. The launchd daemon is designed to be efficient to an extreme because it is the core process. any inefficiency in launchd affects the whole system.
  • launchd jobs: a job is a set of conditions, usually loaded from a plist file. If you imagine that launchd has a refrigerator with a whiteboard attached to it, a job is a note on that whiteboard (e.g., 'buy carrots', 'go to dentist at 3:00', 'when Mary arrives, start roast'). The whiteboard itself doesn't accomplish any of these, it merely lays out what needs to be done under what conditions.
  • launched processes: any process that is launched because one of the conditions in a launchd job has been met. the launchd daemon monitors these processes lightly, taking care of killing off subprocesses when the process quits or restarting the process if it quits unexpectedly (if the job specifies it should do that)


The problem you're having (to continue the analogy) is that you're running out of whiteboard space because you're trying to write the entire recipe for the roast straight onto the whiteboard, rather than making an indirection like "start roast (recipe book pg. 3)". That's what's happening with the globbing: by enabling globbing you're asking launchd to add code for a more complicated file system search (one potentially involving wildcards or shell special characters) and the added code is taking away from the internal space reserved for the job, resulting in truncation of the process code and subsequent errors. Again, launchd is stripped-down for efficiency: there is no meaningful error checking.


The question you want answered is how to convince launchd to allocate more internal whiteboard space for a specific job listing or for all jobs on its list. For all I know the job allocation size is hard-coded into launchd itself (that would be the efficient way to do it, at any rate), but if it's written into a preference file I have no idea where, and if none of the launchctl or launchd.plist keys affects it (which I have doubts about - they seem geared to the resource limits of the resultant process, not of the job), then I don't know where to turn. The launchd daemon itself has no configurable options.


And note: Even if you figure out how to manipulate the job allocation size I recommend you approach it gingerly. launchd is what makes your system run.

Jul 4, 2014 11:31 AM in response to Neville Hillyer

To find the particular limit, you'll want to read the source code behind the execvp system call which is what launchd uses to get the processes going, and also have a look at the source code to launchd to see if there are storage limits there. That latter code is located here:

http://www.opensource.apple.com/source/launchd/launchd-258.22/launchd/src/


I've spent a half-hour or so digging around in the launchctl source code and don't see anything immediately obvious, but don't immediately see the limit, and lack the time to walk the entire code path from the database entry through to the process launch. I've only made a cursory look at the execvp and execve calls.


Alternatively, log a bug report with Apple and request that the limit be better documented.

In the interim and pending any response from Apple, you already know that this approach doesn't work with these long command strings — digging out the details of the limits that you've already identified really doesn't really help after all, where using shorter commands will allow the environment to operate.

Alternatively (and probably more work than it's worth), create and use your own bash script launcher, and use that.

Jul 4, 2014 12:25 PM in response to Neville Hillyer

Thanks for the replies.


I can solve my immediate problem by not using EnableGlobbing.


However it appears that nobody here can:


  1. find where the 4096 byte bash script size limit is documented
  2. say why EnableGlobbing reduces the size limit to 1024 bytes


I would still like to know where these two are documented.


Perhaps there should be a warning about both of these limits and the lack of any reasonable error messages if either is exceeded.

Jul 4, 2014 1:01 PM in response to MrHoffman

Ah, I took a tour of that source code myself, since you pointed it out, and I may have figured it out. in the section where (I'm guessing) it creates a new launchd job (job_new(), in core.c) it keeps filtering the char* that (I'm assuming) contains the string from the Program and/or ProgramArguments keys through the snprintf utility, and snprintf uses a pattern string that allows no more than 1000 characters. From what I'm reading on the snprintf man page the code was probably designed that way to prevent buffer overflow attacks, but the nitty-gritty of programming on this level are (frankly) over my head. But for what it's worth...

Jul 4, 2014 1:06 PM in response to Neville Hillyer

Actually I'm surprised your example runs at all, the reason being you've added the redirect symbol and the file to redirect to as part of the third argument to execvp


As a test try breaking out the redirect and the file as two separate arguments. That is add


<string>></string>

<string>/Users/neville/Desktop/tmp.txt</string>


(note note sure how you add the redirect symbol to the file do whatever has been working)


As for limits to the length of an argument to a program that is set by the OS, I'd be surprised if launched (or any program that execs other programs) would limit it. And the limit (even back in 10.5) was greater then what you are seeing.

Jul 4, 2014 1:10 PM in response to Neville Hillyer

See my last post, which suggests that the limit is hardcoded.


Personally I'd rather not have my OS (and the OS of everyone who owns a mac, anywhere) to slow down because you want Apple to code in error messages on things they could not reasonably expect you'd be trying to do. If you want to work outside the system for your own personal reasons that's fine, but accept the consequences of that independence, and don't expect the rest of us to pay for your peculiarities.


So there! 😝 👿 😎

Jul 4, 2014 1:43 PM in response to twtwtw

twtwtw wrote:


Ah, I took a tour of that source code myself, since you pointed it out, and I may have figured it out. in the section where (I'm guessing) it creates a new launchd job (job_new(), in core.c) it keeps filtering the char* that (I'm assuming) contains the string from the Program and/or ProgramArguments keys through the snprintf utility, and snprintf uses a pattern string that allows no more than 1000 characters. From what I'm reading on the snprintf man page the code was probably designed that way to prevent buffer overflow attacks, but the nitty-gritty of programming on this level are (frankly) over my head. But for what it's worth...


My tests indicate that the limit is normally 4096 bytes but this reduces to 1024 bytes if EnableGlobbing is set to true.

Jul 4, 2014 1:48 PM in response to Frank Caggiano

Frank Caggiano wrote:


Actually I'm surprised your example runs at all, the reason being you've added the redirect symbol and the file to redirect to as part of the third argument to execvp


As a test try breaking out the redirect and the file as two separate arguments. That is add


<string>></string>

<string>/Users/neville/Desktop/tmp.txt</string>


(note note sure how you add the redirect symbol to the file do whatever has been working)


As for limits to the length of an argument to a program that is set by the OS, I'd be surprised if launched (or any program that execs other programs) would limit it. And the limit (even back in 10.5) was greater then what you are seeing.


For some reason I could not get your two separate arguments to work.


I agree there should be very few size limits except for pipe and stack.

Jul 4, 2014 1:57 PM in response to twtwtw

twtwtw wrote:


See my last post, which suggests that the limit is hardcoded.


Personally I'd rather not have my OS (and the OS of everyone who owns a mac, anywhere) to slow down because you want Apple to code in error messages on things they could not reasonably expect you'd be trying to do. If you want to work outside the system for your own personal reasons that's fine, but accept the consequences of that independence, and don't expect the rest of us to pay for your peculiarities.


So there! 😝 👿 😎


Normally all launchd errors are available in the Console. Some may be short on detail and several are misleading but, in my experience, it is unusual not to see all errors reported. Exceeding the 4096 byte limit is the only exception I am aware of. Exceeding the 1024 byte limit by more than a few bytes produces an error even if it appears unrelated to the script.

Is there a launch daemon size limit?

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple ID.