Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FreeBSD service #2333

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions contrib/FreeBSD/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
This folder contains `sopel` and `sopel-default.cfg` designed to be distributed by third parties such as FreeBSD.

`sopel-default.cfg` is a default configuration file for sopel.

The -default prefix is mandatory and will be used as a 'profile'.

The profiles may be changed with the variable `sopel_profiles` in `/etc/rc.conf`. For example:
DtxdF marked this conversation as resolved.
Show resolved Hide resolved

```sh
sysrc sopel_profiles="profile1 profile2 profile3"
```

By default, the configuration directory is `/usr/local/etc`.

The `sopel-profile1.cfg`, `sopel-profile2.cfg` and `sopel-profile3.cfg` files must be stored in the configuration directory.
DtxdF marked this conversation as resolved.
Show resolved Hide resolved

sopel- is mandatory and can be changed with the `sopel_prefix` variable in `/etc/rc.conf` as another FreeBSD service.
half-duplex marked this conversation as resolved.
Show resolved Hide resolved

The service must be installed in `/usr/local/etc/rc.d`:

```sh
cp sopel /usr/local/etc/rc.d
chmod +x /usr/local/etc/rc.d/sopel
```

The default configuration file must be installed in the configuration directory as another profile. For example:

```sh
cp sopel-default.cfg /usr/local/etc
```

If you want to run sopel at startup, the variable `sopel_enable` must be set to `YES`:

```sh
sysrc sopel_enable="YES"
```

If you want to change the python version, the variable `sopel_interpreter` must be changed. For example:
half-duplex marked this conversation as resolved.
Show resolved Hide resolved

```sh
sysrc sopel_interpreter="/usr/local/bin/python3.8"
```

You can set any other variable, but by default, the sopel service has many default values:

```sh
$ egrep -E '^: \$\{sopel_.+:=.+}' sopel
: ${sopel_enable:="NO"}
: ${sopel_piddir:="/var/run/sopel"}
: ${sopel_confdir:=/usr/local/etc}
: ${sopel_flags:=--config-dir "${sopel_confdir}"}
: ${sopel_program:="/usr/local/bin/sopel"}
: ${sopel_user:=${name}}
: ${sopel_interpreter:=/usr/local/bin/python3.9}
: ${sopel_profiles:=default}
: ${sopel_prefix:=sopel-}
: ${sopel_output:=/dev/null}
```
120 changes: 120 additions & 0 deletions contrib/FreeBSD/sopel
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#!/bin/sh

# PROVIDE: sopel
# REQUIRE: FILESYSTEM NETWORKING usr

. /etc/rc.subr

name="sopel"
desc="Simple, easy-to-use, open-source IRC utility bot, written in Python"
rcvar="${name}_enable"
start_cmd="sopel_start"
stop_cmd="sopel_stop"
restart_cmd="sopel_restart"
status_cmd="sopel_status"
configure_cmd="sopel_configure"
extra_commands="configure"

load_rc_config "${name}"

: ${sopel_enable:="NO"}
: ${sopel_piddir:="/var/run/sopel"}
: ${sopel_confdir:=/usr/local/etc}
: ${sopel_flags:=--config-dir "${sopel_confdir}"}
: ${sopel_program:="/usr/local/bin/sopel"}
: ${sopel_user:=${name}}
: ${sopel_interpreter:=/usr/local/bin/python3.9}
: ${sopel_profiles:=default}
: ${sopel_prefix:=sopel-}
: ${sopel_output:=/dev/null}

sopel_start()
{
local profile

profile="$1"; shift

echo "Starting sopel profile '${profile}'." && sleep 1
/usr/sbin/daemon \
-o "${sopel_output}" \
-t "${desc}" \
-u "${sopel_user}" \
${sopel_interpreter} \
${sopel_program} start ${sopel_flags} \
-c "${sopel_prefix}${profile}" $@
}

sopel_stop()
{
local pid pidfile profile

profile="$1"; shift

pidfile="${sopel_piddir}/${sopel_prefix}${sopel_prefix}${profile}.pid"
if ! [ -f "${pidfile}" ]; then
return 1
fi

pid=`cat ${pidfile}`

echo "Stopping sopel profile '${profile}'."
/usr/sbin/daemon \
-o "${sopel_output}" \
${sopel_interpreter} \
${sopel_program} stop ${sopel_flags} \
-c "${sopel_prefix}${profile}" $@

wait_for_pids $pid
}

sopel_restart()
{
local profile

profile="$1"; shift

run_rc_command stop "${profile}" $@ &&
sleep 1 &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Necessary?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sleep 1?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - will stop ever return before sopel has finished stopping? If not, is there a reason for the sleep?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, some IRC servers may block an IP with two or more simultaneous connections to prevent flooding. The message Session limit exceeded should be printed in the IRC client. I will use sleep 1 because daemon(8) will run sopel in the background without waiting. I know sopel has a flag called --fork, but the daemon is convenient in context because the output is redirected to /dev/null (sopel already prints the output to /var/log/sopel, so there is no need to have two outputs).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair, but I think that belongs in the start function - and it looks like there already is one there, making this one unnecessary, unless I'm misunderstanding run_rc_command or something.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to a slow connection or a network problem that delays a packet before disconnecting it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally Sopel sends a QUIT message and waits before exiting. Unless this stop doesn't wait for sopel to exit, or you know of routine cases I don't, you should be able to assume that if the process exits you're all set.

def quit(self, message: Optional[str] = None) -> None:

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I mentioned in another comment, the service uses daemon(8) which does not wait for the program to run.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you agree with the change I mentioned?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting two:
echo;sleep;fork;echo;sleep;fork;echo;sleep;fork
Restarting two:
stop;wait_for_pids;sleep;echo;sleep;fork;stop;wait_for_pids;sleep;echo;sleep;fork

So for each, to start it simply waits 1 second and then forks, for a total delay between instances of one second.
To stop, it's sending a signal, waiting for Sopel to exit, waiting one second, then in start it's waiting another second, then forking the new one. The total delay here is 2 seconds between each instance.

I still don't see why there needs to be twice the delay when restarting vs starting.

run_rc_command start "${profile}" $@
}

sopel_status()
{
local profile pid

profile="$1"; shift

pid=`check_pidfile \
"${sopel_piddir}/${sopel_prefix}${sopel_prefix}${profile}.pid" \
"${sopel_program}" \
"${sopel_interpreter}"`

if [ -n "${pid}" ]; then
echo "Sopel profile '${profile}' is running as pid ${pid}."
else
echo "Sopel profile '${profile}' is not running."
fi
}

sopel_configure()
{
local profile

profile="$1"; shift

echo "Configuring profile '${profile}'..."

${sopel_interpreter} \
${sopel_program} configure ${sopel_flags} \
-c "${sopel_confdir}/${sopel_prefix}${profile}" $@
}

cmd="$1"; shift
for profile in $sopel_profiles; do
if ! [ -f "${sopel_confdir}/${sopel_prefix}${profile}.cfg" ]; then
echo "Sopel profile '${profile}' does not exist."
continue
fi

run_rc_command "${cmd}" "${profile}" $@
done
26 changes: 26 additions & 0 deletions contrib/FreeBSD/sopel-default.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#
DtxdF marked this conversation as resolved.
Show resolved Hide resolved
# IMPORTANT NOTES!
# You must delete the not_configured line in order for the bot to work,
# otherwise it will refuse to start.
#
# You must create homedir. Sopel should create the others directories:
# mkdir -p $homedir
# The service file has a variable called 'sopel_user'. This user name
# must exist. After creating the user, the owner of the logdir,
# pid_dir and homedir directories must be changed. By default,
# the value is 'sopel':
# chown sopel:sopel $logdir
# chown sopel:sopel $pid_dir
# chown sopel:sopel $homedir
half-duplex marked this conversation as resolved.
Show resolved Hide resolved
#
[core]
nick=sopel
not_configured=True
host=irc.libera.chat
port=6697
use_ssl=True
verify_ssl=True
owner=
logdir=/var/log/sopel
pid_dir=/var/run/sopel
homedir=/var/db/sopel