The macosxhints Forums

The macosxhints Forums (http://hintsforums.macworld.com/index.php)
-   UNIX - General (http://hintsforums.macworld.com/forumdisplay.php?f=16)
-   -   can't send email via script using launchd? (http://hintsforums.macworld.com/showthread.php?t=94302)

edjusted 09-24-2008 01:31 AM

can't send email via script using launchd?
 
I'm trying to send an email via shell script. e.g. my shell script is something like this:
#!/bin/sh
echo testing | mail -s 'this is a test' user@domain.com

My launchdaemon runs the script: /usr/local/scripts/testscript.sh

I *know* the script is working, because I added some debugging (I added a line: echo is this working and sure enough, "is this working" is showing up in the console) but the email part kicks off an error message about 30 seconds later: Stray process with PGID equal to this dead job: PID ### PPID 1 sendmail

The script *works* when I run it with a cron job. But why wouldn't it work as a launchd item? And why is it I'm only having problems with the email segment of my scripts? I've tried other scripts and they all seem to work fine.

Hal Itosis 09-24-2008 11:16 AM

What is the path to the plist?
What owner:group does it have?

Post the plist.

-HI-

edjusted 09-24-2008 12:40 PM

Here're the permissions:
-rw-r--r-- 1 root wheel 386 Sep 24 09:28 com.edw.test.plist

The path is /Library/LaunchDaemons/com.edw.test.plist

Here's the plist. If it makes any difference, I created it using Lingon.
<?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>Label</key>
<string>com.edw.test</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/scripts/testscript.sh</string>
</array>
<key>StartInterval</key>
<integer>1800</integer>
</dict>

cwtnospam 09-24-2008 01:08 PM

Quote:

Originally Posted by edjusted (Post 495233)
If it makes any difference, I created it using Lingon.

Did you create it as a System Agent or System Daemon? I think you want it to be under My Agents.

Hal Itosis 09-24-2008 01:50 PM

Quote:

Originally Posted by edjusted (Post 495233)
Here're the permissions:
-rw-r--r-- 1 root wheel 386 Sep 24 09:28 com.edw.test.plist
The path is /Library/LaunchDaemons/com.edw.test.plist

As cwtnospam said, it should work nicely in your user ~/Library/LaunchAgents folder,
(with owner:group = you:admin ... or you:you, whatever).

Is the plist actually loading?
i.e., does the job appear in the launchctl list?

For the location you have now:
sudo launchctl list |grep edw

--

If you move it to ~/Library/LaunchAgents:
launchctl list |grep edw

Try force loading it:
launchctl load -w ~/Library/LaunchAgents/com.edw.test.plist

Or kickstarting it:
launchctl start com.edw.test

cwtnospam 09-24-2008 01:59 PM

Quote:

Originally Posted by edjusted (Post 495161)
I *know* the script is working, because I added some debugging (I added a line: echo is this working and sure enough, "is this working" is showing up in the console)

This is what had me thinking that the script is running, but as the wrong user.

edjusted 09-24-2008 03:48 PM

re wrong user: sorry I didn't make it clear, I *want* the script to run as root.

And I tried another piece of debugging: I put in an "echo $(whoami)" statement in the script, and the console is showing "root", so it seems like it *is* running as root.

So it appears the script *is* running, and it *is* running as root. But there seems to be some sort of problem specific to sending an email. Again, if I run the script using *cron*, it *works*. I'm just attempting to transfer my cron jobs over to launchd like a good Apple user.

tw 09-24-2008 04:42 PM

errr... what are the permissions on the file '/usr/local/scripts/testscript.sh'? I think you'd need it to be owned by root, and have the execute bits set, though there are better Unix people out there than me.

edjusted 09-24-2008 04:46 PM

The script is owned by root and is executable. It *does* run, at least the non-email part of it runs. Here're the details in case I'm missing something:

-rwxr-xr-x 1 root wheel 286 Sep 23 22:20 testscript.sh

tw 09-24-2008 04:56 PM

odd. I think we need more context. why do you want this job to be run as root? generally speaking, the system launchdaemon folder is for very low-level jobs that need to be run out of sight (for reasons of security and system stability). normally I'd expect you to put user-created plists in the system launchagents folder, rather than the launchdaemons folder. what happens if you move it over there? and what are you trying to accomplish in the long term?

edjusted 09-24-2008 05:54 PM

Well, basically the end goal is to run a script that checks the RAID on the machine via "diskutil checkraid" and to send an email alert if it detects a problem. I'm running it as root simply because I had problems running it as an admin. Yes, I'm aware of the security implications, but right now I'd be happy with something that simply works.

(Oh, and yes, I know that my script is supposed to send email *only* if it detects an error. For troubleshooting purposes, I've been forcing the email to send regardless of whether or not there's an error.)

The script *works* when run as a cron job. The *exact same* script chokes when run as a launchd process. And in the troubleshooting process, I'm determining that for some reason, cron doesn't mind sending out email, but launchd balks. In fact, launchd balks at sending email even in such a simple script (see my first post). And again, cron has no problems. Cron is running the script as root, and as far as I can tell, launchd is also running the script as root.

So that's the problem. Maybe it'd be better worded as: why does launchd not want to send email?

edjusted 09-24-2008 05:55 PM

And maybe I should also be asking, can anyone else replicate this problem? The machine in question is running OS X 10.5.5 server, but the problem has existed for all versions of 10.5.

tw 09-24-2008 10:18 PM

ok, after a bit of poking around on the internet, and a bit of experimentation, I've gotten this to work correctly (from within my user account, mind you - there may still be issues involved with running it as a lower-level daemon).

add the following key (in red) to your plist:
Code:

<?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>Label</key>
  <string>com.edw.test</string>
  <key>ProgramArguments</key>
  <array>
      <string>/usr/local/scripts/testscript.sh</string>
  </array>
  <key>StartInterval</key>
  <integer>1800</integer>
  <key>AbandonProcessGroup</key>
  <true/>
</dict>

should do the trick.

edjusted 09-24-2008 11:39 PM

Hot damn, that worked! So bizarre, but great. Thank you!

hayne 09-25-2008 12:51 AM

The reason for the AbandonProcessGroup is explained in this Apple tech note:
http://developer.apple.com/technotes/tn2005/tn2083.html
Quote:

Originally Posted by Apple
Careful With That Fork, Eugene
Starting in Mac OS X 10.5 launchd works to garbage collect any child processes of a launchd daemon or agent process when that process quits. Specifically, when a launchd daemon or agent quits, launchd will send a SIGTERM to the associated process group.

This can cause problems if you're developing a launchd daemon that creates a child process to run some other program (by the traditional fork and exec combination, or via posix_spawn). The child process will inherit its process group ID from your daemon. If your daemon quits before the child process, the child process will receive a SIGTERM because it's in the same process group as your daemon.

There are a number of ways to avoid being tripped up by this, listed below in order of most- to least-preferable.

run the 'child' via launchd — If you make the 'child' a separate launchd job, everything will Just Work™.

create a new session for the child — If you must continue to create the child process manually, have the child process run in a new session (and, consequently, in a new process group) by calling setsid.

use the AbandonProcessGroup property — If you add this property to your program's property list file, launchd will not attempt to garbage collect your child processes.

Note: There are good technical reasons for launchd doing this garbage collection (r. 5501131). Specifically, if a daemon just spawns a child process without trying to isolate it from the daemon's environment, the child process will inherit things that it should not be inheriting, like a reference to the daemon's controlling terminal. Later on, when the daemon quits, the child is still running, and still holding on to that reference. If the controlling terminal closes, the child will get an unexpected SIGHUP, for which the default disposition is to terminate the process.

Traditionally you would isolate the child process from the daemon by having the child call daemon. And, if you did this, the child would indeed end up in a separate process group, and would not be garbage collected by launchd. Keep in mind, however, that the daemon routine is officially deprecated in Mac OS X 10.5 and later.


mnewman 09-25-2008 06:16 PM

Hayne, thanks for the link and info. I wonder if you'd be willing to explain a bit more about this issue.

I've got a similar script which I've been running via cron. I was thinking of modernizing and changing that to launchd.

Am I correct in assuming that in the OP's case the child process is 'mail'? If so, how would he go about running mail via launchd as suggested by the technote you quoted?

tw 09-25-2008 07:59 PM

Quote:

Originally Posted by mnewman (Post 495481)
Am I correct in assuming that in the OP's case the child process is 'mail'? If so, how would he go about running mail via launchd as suggested by the technote you quoted?

not to step on Hayne's toes, but yes, the mail call would be the child process in this case. I don't think you can call it directly from launchd, or create a new session id for it, without going to ridiculous lengths. as I understand launchd, it works like this:
  • a launchd job is a set of conditional triggers loaded into launchd from plists. when the correct conditions are met...
  • launchd spawns a process based on whatever is in the Program or ProgramArguments keys of the plist.
  • launchd waits until the process returns, then it closes the process and resets the job to wait for the next trigger
  • if the spawned process creates any child processes, Launchd keeps track of them and closes them when it closes the main process

now the problem in this case, I think, is that sendmail returns a completion value before it has actually finished sending the email, passing on that 'actually sending' part to some sub-process (probably to keep the system from being hung up while sendmail negotiates with remote hosts). this causes launchd to terminate the process and its spawned children (i.e. that process that's still trying to send the email). since sendmail is prewritten code, there's no way (that I know of) to extract out the sub-process that actually sends the email. you could try using the -I option (forcing mail into interactive mode), which might keep sendmail from returning a completion value too early, but that might generate different errors.

really, the above technote is for people who are writing their own daemons, who can explicitly construct them as separate launchd items, or make sure they are properly detached from the parent process; for retroactive use with programs like sendmail the 'AbandonProcessGroup' key is probably the only choice available.

hayne 09-25-2008 08:48 PM

An alternative might also be to just have the script sleep for a bit (say 30 seconds) so that the mail has completed before the script finally finishes.
But using the AbandonProcessGroup setting is cleaner.

mnewman 09-25-2008 09:27 PM

Please excuse my ignorance, but I'm a rank amateur when it comes to this stuff.

The OP's script uses the 'mail' command. I thought that all 'mail' did was compose the message as a file and write it to the mail drop box (/var/spool/postfix/maildrop). Launchd watches that directory and runs the postfix 'master' process when it detects a change. So, the 'mail' process in the script wouldn't be delayed by communicating with servers or whatever. Shouldn't it just return a completion code when the file is written and not spawn any daughter processes?

tw 09-25-2008 10:15 PM

Quote:

Originally Posted by mnewman (Post 495503)
Please excuse my ignorance, but I'm a rank amateur when it comes to this stuff.

The OP's script uses the 'mail' command. I thought that all 'mail' did was compose the message as a file and write it to the mail drop box (/var/spool/postfix/maildrop). Launchd watches that directory and runs the postfix 'master' process when it detects a change. So, the 'mail' process in the script wouldn't be delayed by communicating with servers or whatever. Shouldn't it just return a completion code when the file is written and not spawn any daughter processes?

well, since the solution worked, this must be the problem (don't you just love the scientific method? :) )

my guess would be that mail triggers the postfix master directly, rather than waiting for launchd to do it. that would make the pfm a child process of mail, rather than of launchd. but I can't seem to find any documentation of the inner workings of mail to confirm that.

mnewman 09-25-2008 10:24 PM

Quote:

Originally Posted by tw (Post 495510)
my guess would be that mail triggers the postfix master directly, rather than waiting for launchd to do it.

Right you are. I just sent mail from the command line and the mail went out immediately.

hayne 09-26-2008 12:28 AM

Quote:

Originally Posted by tw (Post 495510)
my guess would be that mail triggers the postfix master directly, rather than waiting for launchd to do it. that would make the pfm a child process of mail, rather than of launchd. but I can't seem to find any documentation of the inner workings of mail to confirm that.

Use the Source Luke !
Quote:

Originally Posted by from mail_cmds-22/mail/send.c
Code:

        /*
        * Fork, set up the temporary mail file as standard
        * input for "mail", and exec with the user list we generated
        * far above.
        */
        pid = fork();
        if (pid == -1) {
                warn("fork");
                savedeadletter(mtf);
                goto out;
        }
        if (pid == 0) {
                sigset_t nset;
                (void)sigemptyset(&nset);
                (void)sigaddset(&nset, SIGHUP);
                (void)sigaddset(&nset, SIGINT);
                (void)sigaddset(&nset, SIGQUIT);
                (void)sigaddset(&nset, SIGTSTP);
                (void)sigaddset(&nset, SIGTTIN);
                (void)sigaddset(&nset, SIGTTOU);
                prepare_child(&nset, fileno(mtf), -1);
               
                if ((cp = value("sendmail")) != NULL)
                        cp = expand(cp);
                else
                        cp = _PATH_SENDMAIL;
                execv(cp, namelist);
               

                warn("%s", cp);
                _exit(1);
        }
        if (value("verbose") != NULL)
                (void)wait_child(pid);
        else
                free_child(pid);
out:
        (void)Fclose(mtf);
}


The 'fork', followed soon after by an 'exec' is the way that C programs start a child process. You see above that the child process that is 'exec'd is _PATH_SENDMAIL
which is defined in Libc-498/include/paths.h as:
#define _PATH_SENDMAIL "/usr/sbin/sendmail"

AngelHide 10-02-2008 11:36 AM

Hello,

I have the same problem, my script doesn't send e-mail as root with launchd !!
I wrote this in my script but it didn't work.
<key>AbandonProcessGroup</key>
<true/>
:(

tw 10-04-2008 05:19 PM

Quote:

Originally Posted by AngelHide (Post 496509)
Hello,

I have the same problem, my script doesn't send e-mail as root with launchd !!
I wrote this in my script but it didn't work.
<key>AbandonProcessGroup</key>
<true/>
:(

first, that should go in the launchd plist file, not in the script. beyond that, though, we'd need more information. can you show us part of your script, or your plist file?

AngelHide 10-10-2008 08:39 AM

Hello,

Well sorry for the mistake but I wrote it in the launchd plist file not in the script.

Code:

<?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>Label</key>
        <string>test</string>
        <key>ProgramArguments</key>
        <array>
                <string>/usr/local/scripts/test.sh</string>
        </array>
        <key>StartCalendarInterval</key>
        <dict>
                <key>Hour</key>
                <integer>4</integer>
                <key>Minute</key>
                <integer>0</integer>
        </dict>
        <key>AbandonProcessGroup</key>
        <true/>
</dict>
</plist>

and the test script is :

Code:

#!/bin/sh
echo testing | mail -s 'this is a test' "myemail")

I have Leopard Server 10.5.5.
I make the plist file with lingon but it doesn't send e-mails.

rtoads 10-26-2008 01:25 PM

Launchd is for the birds
 
Quote:

Originally Posted by AngelHide (Post 497702)
and the test script is :

Code:

#!/bin/sh
echo testing | mail -s 'this is a test' "myemail")

I have Leopard Server 10.5.5.
I make the plist file with lingon but it doesn't send e-mails.

Your plist file looks correct. I bet the only problem is that you didn't unload the previous plist definition lacking the crucial AbandonProcessGroup key and reload the new one. One of the things that drives me nuts about launchd is that it's almost impossible to figure out which instance of a plist definition is loaded, so I find myself unloading and reloading the job to be absolutely sure. Lingon isn't much help here, suggesting that you either log out and log in OR reboot the server, but not telling you which or why. (Reboot the server? What is this, Windows?)

Some of you Unix gurus out there may see some benefit in dumping cron for this obtuse and difficult scheduler, but the very fact that it is bloody difficult to schedule a simple "email me" script underscores the fact that something that used to be *really* simple is now a big PITA. That is not progress. This discussion is the ONLY place on the web that I was able to turn up the source of this problem. I see there is an Apple technote on it as well, but it should be in the launchd man page. Well, maybe it is, but I wouldn't know:

myserver:~ admin$ man launchd
No manual entry for launchd
myserver:~ admin$ man launchctl
No manual entry for launchctl


Gee thanks, Apple.

NightFlight 09-14-2010 09:26 AM

Everything is easier once you know how.
 
Quote:

Lingon isn't much help here, suggesting that you either log out and log in OR reboot the server, but not telling you which or why. (Reboot the server? What is this, Windows?)
Lingon isn't Apple. Reading about launchctl leads you directly to using launchctl unload/load [-w]. If you are tired of typing the command repeatedly, you can either create an alias or simply add a bash function to your ~/.bashrc.


Quote:

Some of you Unix gurus out there may see some benefit in dumping cron for this obtuse and difficult scheduler, but the very fact that it is bloody difficult to schedule a simple "email me" script underscores the fact that something that used to be *really* simple is now a big PITA. That is not progress.
launchd is a huge advance, but it is not primarily a scheduler. It by happenstance... does schedule and a whole lot more. I'll admit, at first it was a bit daunting and it can create a few "gotchas", but over all it's a better tool. Besides, there's nothing stopping you from installing crond.:rolleyes:

myserver:~ admin$ man launchd
No manual entry for launchd
myserver:~ admin$ man launchctl
No manual entry for launchctl



myserver:~ admin$ man launchd.plist
launchd.plist(5) BSD File Formats Manual launchd.plist(5)

NAME
launchd.plist -- System wide and per-user daemon/agent configuration files

DESCRIPTION
This document details the parameters that can be given to an XML property list that can be loaded into launchd with launchctl.


Dropping launchd into google brings the following link up: http://developer.apple.com/macosx/launchd.html

Quote:

Gee thanks Apple.
Once you lift the skirt of the OS, your on your own. If it gets a bit hard and you have trouble keeping up that's your problem, not theirs. They keep the GUI and the basic tools simple, and that's whats supported. :D

At least the unix layer is accessible. If it wasn't, this would all be moot.

jweisbin 09-29-2010 05:08 PM

Quote:

Originally Posted by edjusted (Post 495161)
I'm trying to send an email via shell script. e.g. my shell script is something like this:
#!/bin/sh
echo testing | mail -s 'this is a test' user@domain.com

My launchdaemon runs the script: /usr/local/scripts/testscript.sh

I *know* the script is working, because I added some debugging (I added a line: echo is this working and sure enough, "is this working" is showing up in the console) but the email part kicks off an error message about 30 seconds later: Stray process with PGID equal to this dead job: PID ### PPID 1 sendmail

The script *works* when I run it with a cron job. But why wouldn't it work as a launchd item? And why is it I'm only having problems with the email segment of my scripts? I've tried other scripts and they all seem to work fine.

I have the same problem, but it only started in OS X Server 10.6 (a clean install), in 10.5.8 this was not a problem. The plists and the scripts are exactly the same between the two. They are loaded in launchctl and running, but just won't send email when run from launchd. When run as a script (they are meant to run as root) they run fine. I do not get any "stray process" messages at all. I do get this message, not sure if its related:

sandboxd[49674] krb5kdc(73) deny mach-lookup com.apple.CoreServices.coreservicesd

Here's the script This is only the portion, the other part does other stuff and is working, so it's not permission of the script):

#!/bin/sh
# email subject
SUBJECT="test script got an error"
# Email To ?
EMAIL="abc@xxxyyyzzz.com"
# Email text/message
EMAILMESSAGE="/tmp/emailmessage.txt"
echo "test script got an error" > $EMAILMESSAGE
# send an email using /bin/mail
/usr/bin/mail -s "$SUBJECT" "$EMAIL" < $EMAILMESSAGE

Here is the plist, in /Library/LauchDaemons:

-rw-r--r--@ 1 root wheel 509 Sep 29 17:00 com.humanworldwide.test.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.humanworldwide.test</string>
<key>LowPriorityIO</key>
<false/>
<key>Program</key>
<string>/Users/humanadmin/Desktop/scripts/test.sh</string>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>5</integer>
<key>Minute</key>
<integer>02</integer>
</dict>
</dict>
</plist>

jweisbin 09-30-2010 09:13 AM

I have the same problem, however only on Snow Leopard Server - not on Leopard Server. The AbandonProcessGroup key does seem to fix the problem, but why this should only happen on SL for me is a mystery.

c-l 10-20-2010 11:45 AM

I had the same problem and solved it with that solution and additionally adapting the postfix.plist (/System/Library/LaunchDaemons/org.postfix.master.plist):

Code:

<?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>AbandonProcessGroup</key>
        <true/>
        <key>Label</key>
        <string>org.postfix.master</string>
        <key>OnDemand</key>
        <false/>

        <key>Program</key>
        <string>/usr/libexec/postfix/master</string>
        <key>ProgramArguments</key>
        <array>
                <string>master</string>
        </array>
        <key>QueueDirectories</key>
        <array>
                <string>/var/spool/postfix/maildrop</string>
        </array>
      <key>RunAtLoad</key>
        <true/>

</dict>
</plist>

HTH


All times are GMT -5. The time now is 05:45 PM.

Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2014, vBulletin Solutions, Inc.
Site design © IDG Consumer & SMB; individuals retain copyright of their postings
but consent to the possible use of their material in other areas of IDG Consumer & SMB.