You are not logged in.

#1 2010-04-09 15:04:25

pointone
Wiki Admin
From: Waterloo, ON
Registered: 2008-02-21
Posts: 379

Packaging best practices: creating users, daemon scripts, and FHS

I finally adopted my first AUR package (mediatomb) and am looking for advice with regards to some packaging best practices.

First: creating users and groups

Other distributions create a mediatomb user and group for use with MediaTomb's daemon mode (and the MediaTomb developers appear to recommend this practice). However, I could find no guideline with respect to user creation. Yes, I understand how I would go about calling groupadd and useradd in an .install script, but what UID and GID should be assigned to the new user and group? The majority of daemons use IDs in the 100s range, whilst regular users are found in the 1000s. How is one to select an appropriate ID and avoid conflict?

I am aware of DeveloperWiki:UID / GID Database, but it seems largely incomplete. What is the general consensus? (I was considering claiming ID 151.)

Second: daemon scripts

The best resource I could find was Writing rc.d scripts, but I would appreciate some clarification. The rc.d script I inherited from the previous maintainer included a check to ensure it was run as root, whereas I see no "official" daemons follow this practice. Is there any particular reason to avoid such checks, or is this simply another KISS idea?

Also, the majority of scripts determine the process ID by calling "pidof" whereas a small minority use "cat $PIDFILE". I opted for a mix of the two: "pidof" will report whether any mediatomb instances are running on the system, whereas "cat $PIDFILE" is specific to the daemon. I imagine some users may run both a daemon instance and another instance as another user. Is there any reason to use one method over another, or simply "whatever works?"

Finally, a minor consistency issue: the majority of daemon scripts "exit 0" at the end, but a handful do not. Should they?

Third: FHS

The sample MediaTomb daemon scripts provided by the developers (for Fedora and Optware) use /etc/mediatomb as the default config directory (which is where the database is created). I opted to use /var/lib/mediatomb instead, but the default configuration will then create both the database and configuration file under /var/lib/mediatomb. Which is the lesser evil (database in /etc or config in /var)?

Thanks in advance for any wisdom you care to share. wink

Last edited by pointone (2010-04-09 15:04:49)


M*cr*s*ft: Who needs quality when you have marketing?

Offline

#2 2010-04-09 16:00:24

Profjim
Member
From: NYC
Registered: 2008-03-24
Posts: 658

Re: Packaging best practices: creating users, daemon scripts, and FHS

pointone wrote:

Also, the majority of scripts determine the process ID by calling "pidof" whereas a small minority use "cat $PIDFILE". I opted for a mix of the two: "pidof" will report whether any mediatomb instances are running on the system, whereas "cat $PIDFILE" is specific to the daemon. I imagine some users may run both a daemon instance and another instance as another user. Is there any reason to use one method over another, or simply "whatever works?"

This is a can of worms. Either method may work most of the time in practice, but neither counts as best practice.


Finally, a minor consistency issue: the majority of daemon scripts "exit 0" at the end, but a handful do not. Should they?

Without that, they'll finish with the exit code of the most-recently executed command, which will normally also be 0. If it might be non-zero, but you want the script to exit 0 anyway, say so explicitly. I bet in a lot of scripts the "exit 0" is not strictly necessary.


The sample MediaTomb daemon scripts provided by the developers (for Fedora and Optware) use /etc/mediatomb as the default config directory (which is where the database is created). I opted to use /var/lib/mediatomb instead, but the default configuration will then create both the database and configuration file under /var/lib/mediatomb. Which is the lesser evil (database in /etc or config in /var)?

I think the least evil is to change the sample scripts to put things in their right place. The "don't touch upstream!" pressure seems weaker for daemon scripts and config files.

Offline

#3 2010-04-10 14:40:35

pointone
Wiki Admin
From: Waterloo, ON
Registered: 2008-02-21
Posts: 379

Re: Packaging best practices: creating users, daemon scripts, and FHS

Thanks for responding; that's essentially what I expected.

The developers don't provide a default configuration file, which is why I didn't include one in /etc. Instead, the software generates a config file (and database) on first run, but only in the specified cfg directory. Hence, either both config and db in /etc or /var. Thinking about it, I believe it will probably be cleaner if I simply generate a config file on my system and include _that_ in the package instead. Problem solved.

Any comment with respect to UIDs/GIDs? That is what I'm most concerned about.


M*cr*s*ft: Who needs quality when you have marketing?

Offline

#4 2010-04-11 00:32:38

Profjim
Member
From: NYC
Registered: 2008-03-24
Posts: 658

Re: Packaging best practices: creating users, daemon scripts, and FHS

I don't know. I'd guess that just adding the uids/gids you want to claim to that wiki page would be the way to go.

Offline

#5 2010-05-20 11:55:34

ancide
Member
Registered: 2009-07-09
Posts: 40

Re: Packaging best practices: creating users, daemon scripts, and FHS

I have a question and instead of creating a new thread I'll just use this one because it's relevant to the topic.

The question is - How would you go about creating a daemon script that executes a shell script, php script or a python script?

This is what I have got so far:

#!/bin/bash

. /etc/rc.conf
. /etc/rc.d/functions

case "$1" in
    start)
        stat_busy "Starting socket bootstrap"
        su -c 'sh /srv/sockets/socket-bootstrap.sh' root
        add_daemon socket-bootstrap
        stat_done
    ;;
    stop)
        stat_busy "Stopping socket bootstrap"

        /usr/bin/killall su -c 'sh /srv/sockets/socket-bootstrap.sh' root
        if [ $? -gt 0 ]; then
            stat_fail
        else
            rm_daemon socket-bootstrap
            stat_done
        fi
    ;;
    *)
        echo "usage: $0 {start|stop}"
esac
exit 0

What happens is that the socket-bootstrap.sh (which contains a loop which keeps some sockets opened) gets runned like if I ran the sh command in the terminal. What I mean by that is that once the start command is sent to the daemon it starts the file but instead of "daemonizing" it, the file gets runned like if I just wrote "sh /srv/sockets/socket-bootstrap.sh". And then obviously the killall command doesn't work on stop either.

Any ideas? smile

Offline

#6 2010-05-20 13:41:27

hbekel
Member
Registered: 2008-10-04
Posts: 311

Re: Packaging best practices: creating users, daemon scripts, and FHS

ancide wrote:

What happens is that the socket-bootstrap.sh (which contains a loop which keeps some sockets opened) gets runned like if I ran the sh command in the terminal. What I mean by that is that once the start command is sent to the daemon it starts the file but instead of "daemonizing" it, the file gets runned like if I just wrote "sh /srv/sockets/socket-bootstrap.sh". And then obviously the killall command doesn't work on stop either.

Any ideas? smile

Don't use su here, it's pointless. Just assume the script is run by root. If it's run by a normal user, it'll just fail, and the user should notice his error.

If the daemon does not daemonize by itself, send it to the background by appending &, just like you would on the terminal. But in this case you won't know whether it has started successfully... see below.

You're trying to killall su, see killall(1). And you can't just killall sh, either... that'll likely kill a lot more processes than you intend.

You should base your rc scripts on /usr/share/pacman/rc-script.proto. The problem is that this proto is not suited for running scripts (sh, ruby, python) as daemons.

Here's a script for a ruby daemon, based on the proto and adjusted as commented:

#!/bin/bash

daemon_name=beepd

. /etc/rc.conf
. /etc/rc.d/functions

get_pid() {
    # we can't use pidof here, since the command is 'ruby' and not $daemon_name.
    # using pgrep with an exact match on the complete commandline, see pgrep(1)
    pgrep -u root -x -f 'ruby /sbin/beepd'
}

case "$1" in
  start)
    stat_busy "Starting $daemon_name daemon"
    PID=$(get_pid)
    if [[ -z "$PID" ]]; then
      [ -f /var/run/$daemon_name.pid ] && rm -f /var/run/$daemon_name.pid
      # RUN
      # instead of just running and checking the return value in $?, we send
      # the script to the background to "daemonize" it:
      $daemon_name &
      #
      # and then use get_pid instead of $? to check if it has started successfully...
      if ! get_pid &>/dev/null; then
        stat_fail
        exit 1
      else
        echo $(get_pid) > /var/run/$daemon_name.pid
        add_daemon $daemon_name
        stat_done
      fi
    else
      stat_fail
      exit 1
    fi
    ;;

  stop)
    stat_busy "Stopping $daemon_name daemon"
    PID=$(get_pid)
    # KILL
    [ ! -z "$PID" ] && kill $PID &> /dev/null
    #
    if [ $? -gt 0 ]; then
      stat_fail
      exit 1
    else
      rm -f /var/run/$daemon_name.pid &> /dev/null
      rm_daemon $daemon_name
      stat_done
    fi
    ;;

  restart)
    $0 stop
    sleep 3
    $0 start
    ;;

  status)
    stat_busy "Checking $daemon_name status";
    ck_status $daemon_name
    ;;

  *)
    echo "usage: $0 {start|stop|restart|status}"
esac

exit 0

Offline

#7 2010-05-21 11:14:48

ancide
Member
Registered: 2009-07-09
Posts: 40

Re: Packaging best practices: creating users, daemon scripts, and FHS

hbekel wrote:

text

Thank you for explaining! smile I adapted your example, but I got an error saying "/etc/rc.d/socket-bootstrap: rad 23: socket-bootstrap: command doesn't exist". I guess it tries to start a command named socket-bootstrap just because I used that as daemon_name. I guess I have to link socket-bootstrap to run "sh /srv/sockets/sockets-bootstrap.sh" or something like that?

#!/bin/bash

daemon_name=socket-bootstrap

. /etc/rc.conf
. /etc/rc.d/functions

get_pid() {
    # we can't use pidof here, since the command is 'ruby' and not $daemon_name.
    # using pgrep with an exact match on the complete commandline, see pgrep(1)
    pgrep -u root -x -f 'sh /srv/sockets/socket-bootstrap.sh'
}

case "$1" in
  start)
    stat_busy "Starting $daemon_name daemon"
    PID=$(get_pid)
    if [[ -z "$PID" ]]; then
      [ -f /var/run/$daemon_name.pid ] && rm -f /var/run/$daemon_name.pid
      # RUN
      # instead of just running and checking the return value in $?, we send
      # the script to the background to "daemonize" it:
      $daemon_name & 
      #
      # and then use get_pid instead of $? to check if it has started successfully...
      if ! get_pid &>/dev/null; then
        stat_fail
        exit 1
      else
        echo $(get_pid) > /var/run/$daemon_name.pid
        add_daemon $daemon_name
        stat_done
      fi
    else
      stat_fail
      exit 1
    fi
    ;;

   etc etc etc

Last edited by ancide (2010-05-21 11:36:28)

Offline

#8 2010-05-21 12:50:27

hbekel
Member
Registered: 2008-10-04
Posts: 311

Re: Packaging best practices: creating users, daemon scripts, and FHS

Basically, yes. Note that my script assumes that a) daemon_name == script filename and b) the script is chmod'ed +x and on PATH. I.e. it can be run by typing beepd & in a terminal.

In your case, I'd just run your script with

sh /srv/sockets/socket-bootstrap.sh &

instead of

$daemon_name &

in the start) section.

Last edited by hbekel (2010-05-21 12:50:54)

Offline

#9 2010-05-21 13:04:11

ancide
Member
Registered: 2009-07-09
Posts: 40

Re: Packaging best practices: creating users, daemon scripts, and FHS

hbekel wrote:

Basically, yes. Note that my script assumes that a) daemon_name == script filename and b) the script is chmod'ed +x and on PATH. I.e. it can be run by typing beepd & in a terminal.

In your case, I'd just run your script with

sh /srv/sockets/socket-bootstrap.sh &

instead of

$daemon_name &

in the start) section.

Alright, that makes sense.

And then how would I stop that background process? Could I use

kill `pgrep sh /srv/sockets/socket-bootstrap.sh &`

or something like that?

Offline

#10 2010-05-21 13:20:44

hbekel
Member
Registered: 2008-10-04
Posts: 311

Re: Packaging best practices: creating users, daemon scripts, and FHS

Please read the script. All's well once get_pid works.

Offline

#11 2010-05-21 14:39:32

Dieter@be
Forum Fellow
From: Belgium
Registered: 2006-11-05
Posts: 2,001
Website

Re: Packaging best practices: creating users, daemon scripts, and FHS

pointone wrote:

Any comment with respect to UIDs/GIDs? That is what I'm most concerned about.

On the developerwiki there is a list of those, with what they should be used for. But I'm not sure how it works for packages in community/aur, etc


< Daenyth> and he works prolifically
4 8 15 16 23 42

Offline

#12 2010-05-21 16:51:54

ancide
Member
Registered: 2009-07-09
Posts: 40

Re: Packaging best practices: creating users, daemon scripts, and FHS

hbekel wrote:

Please read the script. All's well once get_pid works.

Yes, I've read the script. The reason I asked is because it doesn't work. The process is still running in the background after stop. If I do start, then stop and then start again I get a error saying "[Errno 98] Address already in use" since my socket script is already running in the background.

Offline

#13 2010-05-21 17:32:00

hbekel
Member
Registered: 2008-10-04
Posts: 311

Re: Packaging best practices: creating users, daemon scripts, and FHS

Does "stop" report fail? Have you checked whether the pgrep command retrieves the right pid? If so, have you tried to kill that pid from the commandline? Does that work?

Your line:

kill `pgrep sh /srv/sockets/socket-bootstrap.sh &`

is wrong. & is not part of the commandline as seen by ps. Have you read man pgrep? There's a reason it needs those options in get_pid.

Offline

#14 2010-05-22 11:55:36

ancide
Member
Registered: 2009-07-09
Posts: 40

Re: Packaging best practices: creating users, daemon scripts, and FHS

hbekel wrote:

Does "stop" report fail? Have you checked whether the pgrep command retrieves the right pid? If so, have you tried to kill that pid from the commandline? Does that work?
Your line:

kill `pgrep sh /srv/sockets/socket-bootstrap.sh &`

is wrong. & is not part of the commandline as seen by ps. Have you read man pgrep? There's a reason it needs those options in get_pid.

Stop seems to report fail and pgrep doesn't get the right pid.

The thing is that I just want to run one python file which has a loop where it listens to socket connections. And I want to run that in the background as a daemon. I'm notfamiliar with bash and best practises in terms of creating daemons, packages and what not. So maybe it's easier if I just show you my three files.

Here's my rc.d file (/etc/rc.d/chaos-service):

#!/bin/bash

daemon_name=chaos-service

. /etc/rc.conf
. /etc/rc.d/functions

get_pid() {
    pgrep -u root -x -f 'python /usr/lib/chaos-service/bootstrap.py'
}

case "$1" in
  start)
    stat_busy "Starting $daemon_name daemon"
    PID=$(get_pid)
    if [[ -z "$PID" ]]; then
      [ -f /var/run/$daemon_name.pid ] && rm -f /var/run/$daemon_name.pid
      $daemon_name &
      if ! get_pid &>/dev/null; then
        stat_fail
        exit 1
      else
        echo $(get_pid) > /var/run/$daemon_name.pid
        add_daemon $daemon_name
        stat_done
      fi
    else
      stat_fail
      exit 1
    fi
    ;;

  stop)
    stat_busy "Stopping $daemon_name daemon"
    PID=$(get_pid)
    [ ! -z "$PID" ] && kill $PID &> /dev/null
    if [ $? -gt 0 ]; then
      stat_fail
      exit 1
    else
      rm -f /var/run/$daemon_name.pid &> /dev/null
      rm_daemon $daemon_name
      stat_done
    fi
    ;;

  restart)
    $0 stop
    sleep 3
    $0 start
    ;;

  status)
    stat_busy "Checking $daemon_name status";
    ck_status $daemon_name
    ;;

  *)
    echo "usage: $0 {start|stop|restart|status}"
esac

exit 0

Here's my python file (/usr/lib/chaos-service/bootstrap.py):

#!/usr/bin/python

while True:
   print 'Parsing socket'

Then I have /usr/bin/chaos-service which is just empty because I wasn't sure what to do with it. If I was going to invoke the python command in that file or inte rc.d file.


Right now when I do /etc/rc.d/chaos-service start I get fail.

I really appreciate your help, because I am totally lost. hmm

Last edited by ancide (2010-05-22 11:56:15)

Offline

#15 2010-05-22 12:36:46

hbekel
Member
Registered: 2008-10-04
Posts: 311

Re: Packaging best practices: creating users, daemon scripts, and FHS

Replace "$daemon_name &" on line 17 of the rc-script with "python /usr/lib/chaos-service/bootstrap.py &".

This will give you a "daemon" that prints "Parsing socket" forever... (hint)

I suggest you try to read and understand the rc script... As a python programmer, you should be able to guess, or find out by reading a bash tutorial, googling, etc. You won't get this to work if you lack the most basic understanding of bash, daemons, pgrep, etc... I've given you more than enough information and help already... You'll get better help if you ask smart questions. Don't be a help vampire. No offense, but it's getting tedious...:)

Offline

#16 2010-05-23 14:07:10

ancide
Member
Registered: 2009-07-09
Posts: 40

Re: Packaging best practices: creating users, daemon scripts, and FHS

hbekel wrote:

Replace "$daemon_name &" on line 17 of the rc-script with "python /usr/lib/chaos-service/bootstrap.py &".

This will give you a "daemon" that prints "Parsing socket" forever... (hint)

I suggest you try to read and understand the rc script... As a python programmer, you should be able to guess, or find out by reading a bash tutorial, googling, etc. You won't get this to work if you lack the most basic understanding of bash, daemons, pgrep, etc... I've given you more than enough information and help already... You'll get better help if you ask smart questions. Don't be a help vampire. No offense, but it's getting tedious...:)

Thanks.

Why I wrote in this thread is because I didn't find any information about creating a daemon for archlinux except for this thread and the wiki-page. Basically this was my last resort after many trial and errors and trying to find more information. I thought that the forum was created for this reason, to get help when you have tried your best and gotten stuck.

Anyway, thanks for your feedback, I'll try harder next time.

Offline

Board footer

Powered by FluxBB