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

init.d and systemctl scripts #16

Closed
djkraven opened this issue Feb 6, 2020 · 24 comments
Closed

init.d and systemctl scripts #16

djkraven opened this issue Feb 6, 2020 · 24 comments
Assignees
Labels
component:documentation Improvements or additions to documentation resolution:fixed Fixed type:question User question

Comments

@djkraven
Copy link

djkraven commented Feb 6, 2020

Has anyone put together scripts to start, restart, and stop the service so ustreamer could start at boot or easily managed?

@mdevaev mdevaev self-assigned this Feb 6, 2020
@mdevaev
Copy link
Member

mdevaev commented Feb 6, 2020

Systemctl service configuration
Start ustreamer at boot, restart, check status. This process is designed to be able to start multiple cameras with one service script using an arguments.

Preparation
Confirm ustreamer is in the path to be used and has necessary permissions:

$ ls -l /usr/bin/ustreamer
-rwxr-xr-x 1 root root 253268 Nov 16 21:18 /usr/bin/ustreamer

Create a system user for better security and not run as root:

$ sudo useradd -r ustreamer

Add the system user to the group video allowing the system user access to the device:

$ sudo usermod -a -G video ustreamer

... or manually edit /etc/group.

systemctl creation
Create new service file with @ in the name to use start arguments:

$ sudo vi /etc/systemd/system/[email protected]

Here is an example. You will need to update the options as needed for your setup and use:

[Unit]
Description=uStreamer service
After=network.target
[Service]
Environment="SCRIPT_ARGS=%I"
User=ustreamer
ExecStart=/usr/bin/ustreamer --process-name-prefix ustreamer-%I --log-level 0 --device /dev/video%I --device-timeout=8  --quality 100 --resolution 1920x1080 --desired-fps=29 --host=0.0.0.0 --port=808%I --static /var/www/html/ustreamer-%I/
[Install]
WantedBy=multi-user.target

Enable the service:

$ sudo systemctl enable [email protected]

Enable the service with argument for starting at boot:

$ sudo systemctl enable [email protected]

Start the service:

$ sudo systemctl start [email protected]

The number after the ustreamer@ is the webcam device ex: /dev/video0, /dev/video1, etc. This number can also be used in the other options such as --port, --static, etc.

Verify added service is working:

$ sudo systemctl status [email protected][email protected] - uStreamer-0 service
   Loaded: loaded (/etc/systemd/system/[email protected]; disabled; vendor preset: enabled)
   Active: active (running) since Tue 2020-11-17 09:07:49 EST; 9s ago
 Main PID: 9901 (main)
    Tasks: 7 (limit: 4915)
   CGroup: /system.slice/system-ustreamer.slice/[email protected]
           └─9901 ustreamer-0: /usr/bin/ustreamer --process-name-prefix ustreamer-0 --log-level 0 --device /dev/video0 --device-timeout=8 
--quality 100 --resolution 1920x1080 --desired-fps=29 --host=0.0.0.0 --port=8080 --static /var/wwww/html/ustreamer-%I/

Nov 17 09:07:55 hostname ustreamer[9901]: -- INFO  [39419.673    stream] -- Using TV standard: DEFAULT
Nov 17 09:07:55 hostname ustreamer[9901]: -- INFO  [39419.690    stream] -- Using resolution: 1920x1080
Nov 17 09:07:55 hostname ustreamer[9901]: -- INFO  [39419.690    stream] -- Using pixelformat: YUYV
Nov 17 09:07:55 hostname ustreamer[9901]: -- INFO  [39419.708    stream] -- Using HW FPS: 29 -> 5 (coerced)
Nov 17 09:07:55 hostname ustreamer[9901]: -- INFO  [39419.708    stream] -- Using IO method: MMAP
Nov 17 09:07:55 hostname ustreamer[9901]: -- INFO  [39419.749    stream] -- Requested 5 device buffers, got 5
Nov 17 09:07:55 hostname ustreamer[9901]: -- INFO  [39420.123    stream] -- Capturing started
Nov 17 09:07:55 hostname ustreamer[9901]: -- INFO  [39420.123    stream] -- Using JPEG quality: 100%
Nov 17 09:07:55 hostname ustreamer[9901]: -- INFO  [39420.123    stream] -- Creating pool with 4 workers ...
Nov 17 09:07:55 hostname ustreamer[9901]: -- INFO  [39420.124    stream] -- Capturing ...
lines 1-18/18 (END)

You might want to make sure that each usb port has a specific camera. This is easy to do with udev:

https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name

https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names

For example: https://github.com/pikvm/kvmd/blob/master/configs/os/udev/v0-vga-rpi3.rules#L3

@mdevaev
Copy link
Member

mdevaev commented Feb 10, 2020

Looks like I answered your question :) I've added a link to this issue to README for those who will encounter this problem.

@mdevaev mdevaev closed this as completed Feb 10, 2020
@mdevaev mdevaev added type:feature New feature or request component:documentation Improvements or additions to documentation type:question User question and removed question type:feature New feature or request labels May 4, 2020
@Taudris
Copy link

Taudris commented Aug 31, 2021

Here's a udev rules file I wrote for my C920. I put it in /etc/udev/rules.d/99-video-c920.rules, working on Raspbian buster.

# reference: https://man7.org/linux/man-pages/man7/udev.7.html

ACTION=="remove", GOTO="end"

SUBSYSTEM=="video4linux", SUBSYSTEMS=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="082d", ATTRS{serial}=="XXXXXXXX", ATTR{index}="0", GOTO="is_rear"
GOTO="end"

LABEL="is_rear"
RUN="/usr/bin/logger -t udev /dev/video-rear connected"
OWNER="root",GROUP="video",MODE="0660",SYMLINK="video-rear"
RUN+="/usr/bin/v4l2-ctl --device /dev/video-rear --set-ctrl=exposure_auto_priority=0 --set-ctrl=exposure_auto=1 --set-ctrl=white_balance_temperature_auto=0 --set-ctrl=power_line_frequency=0 --set-ctrl=focus_auto=0"
RUN+="/usr/bin/v4l2-ctl --device /dev/video-rear --set-ctrl=exposure_absolute=55 --set-ctrl=gain=0 --set-ctrl=white_balance_temperature=3100 --set-ctrl=focus_absolute=25 --set-ctrl=sharpness=224"

LABEL="end"

@cthulux
Copy link

cthulux commented Feb 13, 2022

Hi! Thank you for the systemctl script, it works great! Do you know if I can create a systemd socket unit instead of a systemd service unit? I want it to be activated when requested and deactivated after the client disconnects. Is that possible? Thanks!

@mdevaev
Copy link
Member

mdevaev commented Feb 13, 2022

Sup. This is not supported now because it requires some code on the part of ustreamer.

@cthulux
Copy link

cthulux commented Feb 13, 2022

Ok, anyway without it works great! Thanks!

@mdevaev
Copy link
Member

mdevaev commented Feb 13, 2022

@cthulux please create the new issue for this feature, I will add it. I can't promise how soon, but I'll try to find the time.

@mdevaev
Copy link
Member

mdevaev commented Feb 13, 2022

@cthulux Implemented socket activation. Please try it from master branch. Build with make WITH_SYSTEMD=1 and use startup command ustreamer --systemd.

@mdevaev
Copy link
Member

mdevaev commented Feb 13, 2022

@cthulux upd: added option --exit-on-no-clients=N. N is the number of seconds without clients after which ustreamer will finish execution.

@cthulux
Copy link

cthulux commented Feb 14, 2022

Thank you @mdevaev ! This is great! I had an issue I solved by installing libsystemd-dev (just commenting it for anyone else that wants to try it). I'll try it and come back to let you know how it was. Thanks!

@cthulux
Copy link

cthulux commented Feb 14, 2022

Hi @mdevaev, I've been trying to follow online tutorials in order to create the systemd socket file with the new options but I was not very sucessful, I don't know what is missing:

cat /lib/systemd/system/[email protected]
[Unit]
Description=RaspberryPi camera stream
After=network.target
Wants=network.target
Requires=rpicam.socket

[Service]
Type=simple
User=ustreamer
#StandardOutput=socket
PrivateUsers=yes
DeviceAllow=/dev/video0
PrivateTmp=yes
ProtectHome=yes
Restart=on-abnormal
RestartSec=20

ExecStart=/usr/local/bin/ustreamer --process-name-prefix ustreamer-%I --log-level 0 --drop-same-frames=30 --desired-fps 6 --device-timeout=8 --buffers=3 -r 1296x972 --device=/dev/video%I --quality 80 --slowdown --systemd --host 127.0.0.1 --port=8089

[Install]
WantedBy=multi-user.target

And the systemd socket file is:

cat /lib/systemd/system/rpicam.socket
[Unit]
Description=CUPS Scheduler
[email protected]

[Socket]
ListenStream=/run/ustreamer.sock
SocketUser=ustreamer

Accept=yes

[Install]
WantedBy=sockets.target

Thanks!

@mdevaev
Copy link
Member

mdevaev commented Feb 14, 2022

Remove --host 127.0.0.1 --port=8089

@mdevaev
Copy link
Member

mdevaev commented Feb 14, 2022

Also remove Accept=yes

@cthulux
Copy link

cthulux commented Feb 14, 2022

Hi @mdevaev , I followed your suggestions and had to change the service template file to service file only because there was some issue with the template I couldn't manage. Finaly I was able to make the sytemd socket file run without issues but I couldn't connect to it.

I am using Haproxy in order to add TLS encryption and authentication. Previous to adding the systemd socket file, It listened in port 8090 and forwarded it to ustreamer localhost port 8089.

Now I adapted it so it listens on the tcp unix path but I am unable to see any video since it looks like the systemd service is not trigered or I don't know what.

This is the Haproxy which worked previously with "server rpicam 127.0.0.1:8089" and now for the socket systed file I changed to "server rpicam /run/ustreamer.sock":

userlist rpicamcredentials
   user USERNAME password HASHED_PASSWORD

frontend rpilibcam
    bind-process 2-3
    bind :8090 tfo ssl crt /etc/haproxy/certs/ process 2-3 alpn h2,http/1.1 curves X25519:P-256:secp384r1
    bind abns@haproxy-clt7 accept-proxy tfo ssl crt /etc/haproxy/certs/ process 2-3 alpn h2,http/1.1 curves X25519:P-256:secp384r1
    mode http
    option forwardfor
    http-request redirect scheme https unless { ssl_fc }
    http-request auth unless { http_auth(rpicamcredentials) }
    default_backend rpicam_8089

backend rpicam_8089
    mode http
    option forwardfor
    retry-on all-retryable-errors
    #server rpicam 127.0.0.1:8089
    server rpicam /run/ustreamer.sock

This is the systemd service file:

cat /lib/systemd/system/ustreamer.service
[Unit]
Description=Ustreamer RaspberryPi camera stream
After=network.target ustreamer.socket
Wants=network.target
Requires=ustreamer.socket

[Service]
#Environment="SCRIPT_ARGS=%I" 
Type=simple
User=ustreamer
#StandardOutput=socket
PrivateUsers=yes
DeviceAllow=/dev/video0
PrivateTmp=yes
ProtectHome=yes
Restart=on-abnormal
RestartSec=20

ExecStart=/usr/local/bin/ustreamer --process-name-prefix ustreamer-0 --log-level 0 --drop-same-frames=30 --desired-fps 6 --device-timeout=8 --buffers=3 -r 1296x972 --device=/dev/video0 --quality 80 --slowdown --systemd

[Install]
WantedBy=multi-user.target

And this is the systemd socket file:

cat /lib/systemd/system/ustreamer.socket
[Unit]
Description=Ustreamer socket unit
PartOf=ustreamer.service

[Socket]
ListenStream=/run/ustreamer.sock
SocketUser=ustreamer
SocketMode=755

[Install]
WantedBy=sockets.target

I added ustreamer user for both systemd unit files, also added haproxy user to the ustreamer group and just in case made the ustreamer socket permissions 755 so haproxy can read the socket (with option SocketMode=755).

ls -lrth /run/ | grep -i ustreamer
srwxr-xr-x  1 ustreamer ustreamer    0 Feb 14 11:57 ustreamer.sock

After I login with the credentials I defined in haproxy basic authentication to https://host:8090 I can see the error
503 Service Unavailable

Do you know if there is another way (instead of Haproxy) to test if the /run/ustreamer.sock.

If this is something dificult, I can use the previous configuration without socket file which worked really fine.

Thanks a lot!

@mdevaev
Copy link
Member

mdevaev commented Feb 14, 2022

Try curl --unix /run/ustreamer.sock http://127.0.0.1:8080/state.

I used the systemd debugging tool to check. Everything works:

$ systemd-socket-activate -l /tmp/foo.sock ./ustreamer --systemd
$ curl --unix /tmp/foo.sock http://127.0.0.1:8080/state

@cthulux
Copy link

cthulux commented Feb 15, 2022

Thank you @mdevaev! It works! Now I'll have to figure out myself howto make it work with Haproxy. :)

@mdevaev
Copy link
Member

mdevaev commented Feb 15, 2022

Glad to hear it!

@robertsamples
Copy link

I'm having some trouble trying to parse the comments here and am not sure if I am having the same issues that others are having.

after enabling the script with systemctl I recieve this error:
Failed to enable unit: File multi-user.target: Identifier removed
I found people discussing changing the target to default.target which caused an error when enabling the script but by the command to the following I could enable, start and get the status with log files indicating it was working:
sudo systemctl enable ustreamer@user.service

Unfortunately when I try to connect to the specified port I can't connect as I can when I manually start ustreamer. Do you have any suggestions? Thank you!

@ZyberSE
Copy link

ZyberSE commented Mar 31, 2023

I tried setting up the socket but failed, anyone care to give me a step-by-step example? Would be an nice addition to the otherwise well written guide.

@cthulux
Copy link

cthulux commented Apr 5, 2023

Hi @ZyberSE, It is a bit difficult for me to answer this since I am not currently using it.

This are the instructions I was able to recover from what I did to get it working:

Step 1: Download latest version and compile with "WITH_SYSTEMD=1". You don't need to use git, since current releases may include the systemd compilation flag. At the time I did it, I had to use git due to it was a new feature @mdevaev implemented:

git fetch --depth=1 https://github.com/pikvm/ustreamer
make WITH_SYSTEMD=1
make install

If you need to update it later:

git fetch
git reset --hard HEAD
git merge '@{u}'
make WITH_SYSTEMD=1
make install

Create specific camera user:

sudo useradd -r ustreamer; sudo usermod -a -G video ustreamer

Check if you have the systemd services for ustreamer:

root@rpi4:/home/pi# systemctl list-unit-files | grep -i ustream
ustreamer.service                      disabled        enabled
[email protected]                     disabled        enabled
ustreamer.socket                       masked          enabled
root@rpi4:/home/pi#

If you have those, then just start/enable the socket:

systemctl daemon-reload && sudo systemctl enable –now ustreamer.socket

Else, create the systemd files. Here you are some systemd examples (they are overloaded with options that certainly are not needed and others may not be up to date, that's why I said examples):

root@rpi4:/home/pi# systemctl cat [email protected]

# /lib/systemd/system/[email protected]
[Unit]
Description=Ustreamer RaspberryPi camera stream
After=network.target 
Wants=network.target

[Service]
Environment="SCRIPT_ARGS=%I"
Type=simple
User=ustreamer
PrivateUsers=yes
DeviceAllow=/dev/video0
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=strict
Restart=on-abnormal
RestartSec=5
NoNewPrivileges=yes

# SIN systemd socket #
ExecStart=/usr/local/bin/ustreamer --process-name-prefix ustreamer-%I --log-level 0 --drop-same-frames=30 --desired-fps 6 --device-timeout=8 --buffers=3 -r 1296x972 --d>

[Install]
WantedBy=multi-user.target

root@rpi4:/home/pi# systemctl cat ustreamer.service

# /lib/systemd/system/ustreamer.service
[Unit]
Description=Ustreamer RaspberryPi camera stream
After=network.target ustreamer.socket
Wants=network.target
Requires=ustreamer.socket
KillMode=control-group

[Service]
#Environment="SCRIPT_ARGS=%I"
NoNewPrivileges=yes
Type=simple
User=ustreamer
ProcSubset=pid
ProtectProc=noaccess
ProtectClock=yes
#StandardOutput=socket
PrivateUsers=yes
DeviceAllow=/dev/video0
InaccessiblePaths=/etc/
InaccessiblePaths=/boot/
InaccessiblePaths=/home/
PrivateTmp=yes
ProtectHome=tmpfs
ProtectSystem=strict
#ProtectHome=yes
Restart=on-abnormal
#RestrictNetworkInterfaces=lo
PrivateNetwork=yes
RestartSec=10
#NoExecPaths=/
UMask=0007
#ExecPaths=/usr/local/bin/ustreamer
ExecStart=/usr/local/bin/ustreamer --process-name-prefix ustreamer-0 --log-level 0 --drop-same-frames=30 --desired-fps 6 --device-timeout=8 --buffers=3 -r 1296x972 --device=/dev/video0 --quality 80 --slowdown --exit-on-no-clients=300 --systemd

[Install]
WantedBy=multi-user.target

This is were you create the socket file, please edit and replace "/var/lib/haproxy/ustreamer.sock" with your socket name
root@rpi4:/home/pi# cat /lib/systemd/system/ustreamer.socket

[Unit]
Description=Ustreamer socket unit
PartOf=ustreamer.service

[Socket]
#Please, your socket here!
ListenStream=/var/lib/haproxy/ustreamer.sock
SocketUser=ustreamer
SocketMode=006

[Install]
WantedBy=sockets.target

I tried setting up the socket but failed, anyone care to give me a step-by-step example? Would be an nice addition to the otherwise well written guide.

@cthulux
Copy link

cthulux commented Apr 5, 2023

I'm having some trouble trying to parse the comments here and am not sure if I am having the same issues that others are having.

after enabling the script with systemctl I recieve this error: Failed to enable unit: File multi-user.target: Identifier removed I found people discussing changing the target to default.target which caused an error when enabling the script but by the command to the following I could enable, start and get the status with log files indicating it was working: sudo systemctl enable ustreamer@user.service

Unfortunately when I try to connect to the specified port I can't connect as I can when I manually start ustreamer. Do you have any suggestions? Thank you!

Hi! I just saw your question today, are you still facing this issue?

@korylek1231
Copy link

korylek1231 commented May 10, 2023

Hi. I Found this thread and decided to create my own autostart script. After some time i manage to make full working and enabling on start. Wanted to share it. I'm running OctoPrint on LUbuntu. Video is from USB camera

[Unit]
Description=uStreamer service
After=network-online.target
Wants=network-online.target // Those two are very important! If u wont add it script doesn't turn on system boot!
Restart=on-failure
[Service]
DeviceAllow=/dev/video0
User=[username]
ExecStart= [_Here i put path to ./ustreamer script. In my case it's "/home/me/ustreamer/src/ustreamer.bin"  with addons like --device=/dev/video0 etc._]
[Install]
WantedBy=default.target

All other things like creating etc. same as @mdevaev mentioned in first post (btw thx for it). If u're gonna use only one camera it better to create script without "@" allias.

@jacob-swanson
Copy link

jacob-swanson commented Jul 19, 2024

I slapped together a quick nixos module for this with socket activation, since there wasn't one in nixpkgs, and figured I'd share it here!

{
  config,
  lib,
  pkgs,
  ...
}:
with lib;
let
  cfg = config.services.ustreamer;
in
{
  options = {
    services.ustreamer = {
      enable = mkEnableOption "ustreamer webcam streamer";

      user = mkOption {
        type = types.str;
        default = "ustreamer";
        description = "ustreamer user name.";
      };

      group = mkOption {
        type = types.str;
        default = "video";
        description = "ustreamer group name.";
      };

      listenStream = mkOption {
        type = types.str;
        default = "/run/ustreamer.sock";
        description = "ustreamer socket.";
      };

      extraArgs = mkOption {
        type = types.str;
        default = "";
        description = "ustreamer cli args";
      };
    };
  };

  config = mkIf cfg.enable {
    users.users = optionalAttrs (cfg.user == "ustreamer") {
      ustreamer = {
        group = cfg.group;
        isSystemUser = true;
      };
    };

    systemd.services.ustreamer = {
      description = "ustreamer webcam streamer";
      after = [
        "network.target"
        "ustreamer.socket"
      ];
      requires = [ "ustreamer.socket" ];
      serviceConfig = {
        User = cfg.user;
        Group = cfg.group;
        ExecStart = "${pkgs.ustreamer}/bin/ustreamer --systemd --exit-on-no-clients 300 ${cfg.extraArgs}";
      };
    };

    systemd.sockets.ustreamer = {
      description = "ustreamer webcam socket";
      partOf = [ "ustreamer.service" ];
      wantedBy = [ "sockets.target" ];
      listenStreams = [ cfg.listenStream ];
    };
  };
}

Then you can use it with nginx+mainsail like this!

services.ustreamer = {
  enable = true;
  extraArgs = "--resolution 1280x720";
};
services.nginx.upstreams.ustreamer = {
  servers = {
    "unix:${config.services.ustreamer.listenStream}" = { };
  };
};
services.mainsail = {
  enable = true;
  nginx = {
    # ...
    locations."/webcam/" = {
      proxyPass = "http://ustreamer/";
      extraConfig = ''
        postpone_output 0;
        proxy_buffering off;
        proxy_ignore_headers X-Accel-Buffering;
      '';
    };
  };
};

@bamhm182
Copy link

bamhm182 commented Aug 1, 2024

@jacob-swanson, your config looks good to me. You should submit it to nixpkgs and help everyone out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component:documentation Improvements or additions to documentation resolution:fixed Fixed type:question User question
Development

No branches or pull requests

9 participants