{"id":12099,"date":"2024-01-20T20:59:16","date_gmt":"2024-01-20T18:59:16","guid":{"rendered":"https:\/\/u-labs.de\/portal\/?p=12099"},"modified":"2024-01-22T16:38:41","modified_gmt":"2024-01-22T14:38:41","slug":"3-wege-1-verbotener-um-programme-auf-dem-raspberry-pi-os-automatisch-zu-starten","status":"publish","type":"post","link":"https:\/\/u-labs.de\/portal\/3-wege-1-verbotener-um-programme-auf-dem-raspberry-pi-os-automatisch-zu-starten\/","title":{"rendered":"3 Wege +1 verbotener, um Programme auf dem Raspberry Pi OS automatisch zu starten"},"content":{"rendered":"<p>Bestimmte Programme oder selbst entwickelte Skripte m\u00f6chte man automatisch zusammen mit dem Raspberry Pi starten und ggf. im Hintergrund laufen lassen. Daf\u00fcr muss nicht zwingend ein vergleichsweise komplexes Systemd-Unit entwickelt werden: Ich zeige dir in diesem Beitrag verschiedene Wege, mit und ohne Docker-Container. Au\u00dferdem einen <em>verbotenen<\/em>, den du besser nicht nutzen solltest, inklusive Erkl\u00e4rung warum.<\/p>\n<h2 class=\"wp-block-heading\">Wie werden Programme automatisch gestartet?<\/h2>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/u-labs.de\/portal\/wp-content\/uploads\/2024\/01\/rpi_boot.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"549\" src=\"https:\/\/u-labs.de\/portal\/wp-content\/uploads\/2024\/01\/rpi_boot-1024x549.jpg\" alt=\"\" class=\"wp-image-12136\" srcset=\"https:\/\/u-labs.de\/portal\/wp-content\/uploads\/2024\/01\/rpi_boot-1024x549.jpg 1024w, https:\/\/u-labs.de\/portal\/wp-content\/uploads\/2024\/01\/rpi_boot-300x161.jpg 300w, https:\/\/u-labs.de\/portal\/wp-content\/uploads\/2024\/01\/rpi_boot-768x412.jpg 768w, https:\/\/u-labs.de\/portal\/wp-content\/uploads\/2024\/01\/rpi_boot-640x343.jpg 640w, https:\/\/u-labs.de\/portal\/wp-content\/uploads\/2024\/01\/rpi_boot-317x170.jpg 317w, https:\/\/u-labs.de\/portal\/wp-content\/uploads\/2024\/01\/rpi_boot.jpg 1067w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n<p>Der Raspberry Pi nutzt eine eigene Firmware, statt des in der X86-Welt \u00fcblichen BIOS\/UEFI. Dies startet den Linux-Kernel. Nach Linux \u00fcbernimmt ein Init-System: Es startet alle weiteren Programme bis zum Anmeldebildschirm und k\u00fcmmert sich um deren Lebenszyklus (Starten, Stoppen, Neu starten usw).<sup data-fn=\"8bfcf139-541c-49bf-b1e7-2e4e4678e14e\" class=\"fn\"><a href=\"#8bfcf139-541c-49bf-b1e7-2e4e4678e14e\" id=\"8bfcf139-541c-49bf-b1e7-2e4e4678e14e-link\">1<\/a><\/sup> Bereits auf einem frisch installierten GNU\/Linux-System laufen einige Prozesse im Hintergrund, wie beispielsweise ein SSH-Server f\u00fcr den Fernzugriff oder DHCP-Client, damit automatisch eine IP-Adresse f\u00fcrs Netzwerk bezogen werden kann. Oder f\u00fcr WLAN-Netzwerke eine Software, welche die g\u00e4ngige WPA-Verschl\u00fcsselung unterst\u00fctzt. Auf Systemen mit grafischer Oberfl\u00e4che finden sich noch deutlich mehr laufende Programme.<\/p>\n<p>Es gibt verschiedene Init-Systeme.<sup data-fn=\"deba6ee3-bdf8-4500-b82d-50da47705c8a\" class=\"fn\"><a href=\"#deba6ee3-bdf8-4500-b82d-50da47705c8a\" id=\"deba6ee3-bdf8-4500-b82d-50da47705c8a-link\">2<\/a><\/sup> Lange Zeit war init.d stark verbreitet, viele Distributionen haben es durch Systemd abgel\u00f6st &#8211; darunter auch Debian und damit das Raspberry Pi OS. Soll ein weiteres Programm automatisch beim Start des Betriebssystems ebenfalls gestartet werden, muss man dies im Init-System eintragen. Systemd ist m\u00e4chtig, dadurch auch relativ komplex. Doch es gibt mehrere Alternativen \u00fcber Programme, die wiederum per Systemd bereites gestartet werden und die M\u00f6glichkeit bieten, dort selbst eigene Software zu starten.<\/p>\n<h2 class=\"wp-block-heading\">Muss ich das \u00fcberhaupt h\u00e4ndisch machen?<\/h2>\n<p>Einsteiger sollten wissen, dass APT-Pakete \u00fcblicherweise bereits alle n\u00f6tigen Dateien bei der Installation mitbringen, um sich als Systemd-Dienst zu registrieren. Dieser hei\u00dft meist identisch wie die Software. W\u00e4hrend der Installation wird teilweise angezeigt, welchen Dienst das Paket anlegt. Folgendes Beispiel zeigt die Ausgabe beim installieren des Webservers Nginx, dieser legt <code class=\"\" data-line=\"\">nginx.service<\/code> an:<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/u-labs.de\/portal\/wp-content\/uploads\/2024\/01\/grafik-35.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"62\" src=\"https:\/\/u-labs.de\/portal\/wp-content\/uploads\/2024\/01\/grafik-35-1024x62.png\" alt=\"\" class=\"wp-image-12100\" srcset=\"https:\/\/u-labs.de\/portal\/wp-content\/uploads\/2024\/01\/grafik-35-1024x62.png 1024w, https:\/\/u-labs.de\/portal\/wp-content\/uploads\/2024\/01\/grafik-35-300x18.png 300w, https:\/\/u-labs.de\/portal\/wp-content\/uploads\/2024\/01\/grafik-35-768x46.png 768w, https:\/\/u-labs.de\/portal\/wp-content\/uploads\/2024\/01\/grafik-35-640x39.png 640w, https:\/\/u-labs.de\/portal\/wp-content\/uploads\/2024\/01\/grafik-35.png 1110w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n<p>Ansonsten kann man sich mit <code class=\"\" data-line=\"\">systemctl --all<\/code> s\u00e4mtlich registrierten <em>Units<\/em> genannten Einheiten anzeigen und diese mit <code class=\"\" data-line=\"\">grep<\/code> durchsuchen:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nu-labs@pi4:~ $ systemctl --all | grep nginx\n  nginx.service       loaded    active   running   A high performance web server and a reverse proxy server\n<\/pre>\n<\/div>\n<p>Oft legen APT-Pakete nicht nur einen passenden Dienst an, sondern aktivieren &amp; starten diesen automatisch nach der Installation.<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nu-labs@pi4:~ $ systemctl status nginx\n\u25cf nginx.service - A high performance web server and a reverse proxy server\n     Loaded: loaded (\/lib\/systemd\/system\/nginx.service; enabled; vendor preset: enabled)\n     Active: active (running) since Wed 2024-01-09 12:06:11 CET; 3min 16s ago\n<\/pre>\n<\/div>\n<p>Ansonsten l\u00e4sst sich der Dienst wie folgt in den Autostart legen und starten, sodass er ohne Neustart des Systems anschlie\u00dfend genutzt werden kann:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nsudo systemctl enable --now nginx\n<\/pre>\n<\/div>\n<p>Anders sieht es aus, wenn man Programme manuell (an der Paketverwaltung vorbei) installiert. Oder eigene Skripte\/Programme geschrieben hat. In diesen F\u00e4llen existiert nat\u00fcrlich kein vorbereiteter Systemd-Dienst. Man m\u00fcsste entweder sich mit Systemd befassen und einen eigenen Entwickeln, oder eine der folgenden Alternativen nutzen. Hierf\u00fcr schauen wir uns im folgenden mehrere M\u00f6glichkeiten an &#8211; sortiert nach der Reihenfolge, wie ich diese empfehle.<\/p>\n<h2 class=\"wp-block-heading\">Beispiel-Szenario<\/h2>\n<p>Zur Demonstration habe ich in Python 3 einen kleinen Webserver geschrieben, der jede Anfrage mit einer Willkommensnachricht beantwortet. Dies l\u00e4uft im Vordergrund, d.H. wenn wir es mit <code class=\"\" data-line=\"\">python webserver.py<\/code> starten, wird die Konsole blockiert. Ziel ist es, dieses Skript automatisiert im Hintergrund zu starten, sodass der Webserver erreichbar ist.<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\nfrom http.server import BaseHTTPRequestHandler, HTTPServer\nimport signal\nimport sys, os\n\nhostName = &quot;0.0.0.0&quot;\nserverPort = 8080\nwebServer = None\n\nclass MyServer(BaseHTTPRequestHandler):\n  def do_GET(self):\n    self.send_response(200)\n    self.send_header(&quot;Content-Type&quot;, &quot;text\/html&quot;)\n    self.end_headers()\n    self.wfile.write(bytes(&quot;Hallo vom Python Webserver&quot;, &quot;utf-8&quot;))\n\ndef terminate(signal,frame):\n  webServer.server_close()\n  sys.exit(0)\n\nif __name__ == &quot;__main__&quot;:\n    signal.signal(signal.SIGTERM, terminate)\n\n    webServer = HTTPServer((hostName, serverPort), MyServer)\n    print(&quot;Server gestartet: http:\/\/%s:%s mit pid %i&quot; % (hostName, serverPort, os.getpid()))\n    webServer.serve_forever()\n<\/pre>\n<\/div>\n<h2 class=\"wp-block-heading\">#1 Einsatz von (Docker)-Containern<\/h2>\n<p>Der Docker-Daemon wird per Systemd gestartet und sorgt wiederum daf\u00fcr, dass alle Container laufen, bei denen das <a href=\"https:\/\/u-labs.de\/portal\/docker-container-im-autostart-restartpolicies-von-docker-statt-systemd-dienst-raspberry-pi\/\">per Policy festgelegt wurde<\/a>. Dar\u00fcber hinaus erlaubt es Docker, ein Programm mit allen Abh\u00e4ngigkeiten zu isolieren. Somit k\u00f6nnen beispielsweise mehrere Python\/PHP Versionen f\u00fcr verschiedene Skripte parallel genutzt werden &#8211; das ist in den meisten F\u00e4llen die insgesamt beste Methode. Lediglich auf besonders ressourcenarmen Systemen f\u00fcr bestimmte Zwecke macht es Sinn, darauf zu verzichten.<\/p>\n<p>Zur Einrichtung von Docker auf dem Raspberry Pi (und Debian GNU\/Linux) habe ich bereits mehrere Beitr\u00e4ge gemacht, in denen sowohl das Konzept hinter Containern, als auch die Installation ausf\u00fchrlich gezeigt wird:<\/p>\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/u-labs.de\/portal\/docker-auf-dem-raspberry-pi-das-sind-die-vorteile-von-containertechnologie-auf-dem-einplatinencomputer\/\">Vorteile von Containertechnologien wie Docker<\/a><\/li>\n<li><a href=\"https:\/\/u-labs.de\/portal\/docker-auf-dem-raspberry-pi-installieren-erste-container-starten-einfach-erklaert\/\">Docker auf dem Raspberry Pi OS (und Debian) installieren<\/a><\/li>\n<li><a href=\"https:\/\/u-labs.de\/portal\/dockerfile-einstieg-fuer-anfaenger-was-ist-ein-dockerfile-und-ein-image-wie-kann-ich-images-anpassen\/\">So schreibst du dein erstes Dockerfile f\u00fcr eigene Images<\/a><\/li>\n<\/ul>\n<p>F\u00fcr unser Beispielszenario gen\u00fcgt ein einfaches Dockerfile, in dem das zuvor angelegte <strong>webserver.py<\/strong> Skript (liegt im gleichen Ordner) in einer kompakten Python3 Umgebung geladen wird: <\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: yaml; title: ; notranslate\" title=\"\">\nFROM python:3-alpine\nWORKDIR \/app\nCOPY webserver.py .\nENV PYTHONUNBUFFERED true\nCMD &#x5B;&quot;python&quot;, &quot;.\/webserver.py&quot;]\n<\/pre>\n<\/div>\n<p><strong>docker-compose.yml:<\/strong><\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: yaml; title: ; notranslate\" title=\"\">\nservices:\n  python-web:\n    build: .\n    mem_limit: 128M\n    restart: always\n    ports:\n      - 8080:8080\n<\/pre>\n<\/div>\n<p>Wichtig ist hierbei die zuvor erw\u00e4hnte Restart-Policy: Durch <code class=\"\" data-line=\"\">restart: always<\/code> wird dieser Container automatisch beim Systemstart gestartet, nachdem wir ihn einmalig erstellen &amp; mit <code class=\"\" data-line=\"\">-d<\/code> im Hintergrund starten. Der einzige Nachteil dieser Variante: Man kann Abh\u00e4ngigkeiten nur innerhalb der docker-compose.yml angeben. Dar\u00fcber hinaus ist es nicht m\u00f6glich, Container in komplexeren Szenarien aufeinander aufbauen zu lassen. In den meisten F\u00e4llen wird das ausreichen. Im Einzelfall m\u00fcsste man ggf. \u00fcber Startskripte die ben\u00f6tigten Abh\u00e4ngigkeiten (etwa ein MySQL Server) abfragen und damit den Start der gew\u00fcnschten Anwendung verz\u00f6gern.<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\ndocker compose up -d\n<\/pre>\n<\/div>\n<p>Nach dem Neustart des Systems:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nu-labs@pi5:~ $ uptime\n 15:14:44 up 1 min,  3 users,  load average: 0.24, 0.18, 0.07\nu-labs@pi5:~ $ docker ps\nCONTAINER ID   IMAGE                    COMMAND                  CREATED         STATUS              PORTS                                       NAMES\na09133f29f95   docker-demo-python-web   &quot;python .\/webserver.\u2026&quot;   4 minutes ago   Up About a minute   0.0.0.0:8080-&gt;8080\/tcp, :::8080-&gt;8080\/tcp   docker-demo-python-web-\n<\/pre>\n<\/div>\n<h2 class=\"wp-block-heading\">#2 Per Crontab<\/h2>\n<p>Der fr\u00fchere Windows-Nutzer in mir w\u00fcrde sie wohl als <em>Aufgabenverwaltung f\u00fcr GNU\/Linux<\/em> bezeichnen: Eben so wie diese erm\u00f6glicht es Cron, bestimmte Programme zu festlegten Uhrzeiten auszuf\u00fchren &#8211; t\u00e4glich um 02:00 Uhr, jede volle Stunde, alle 10 Minuten usw.<sup data-fn=\"5f31b5c7-b478-4921-8ff0-c331675e9844\" class=\"fn\"><a href=\"#5f31b5c7-b478-4921-8ff0-c331675e9844\" id=\"5f31b5c7-b478-4921-8ff0-c331675e9844-link\">3<\/a><\/sup> Mit <code class=\"\" data-line=\"\">crontab<\/code> k\u00f6nnen solche Aufgaben angelegt &amp; ver\u00e4ndert werden.<sup data-fn=\"b427a95c-249b-4ecc-8c99-aeabfd9d989a\" class=\"fn\"><a href=\"#b427a95c-249b-4ecc-8c99-aeabfd9d989a\" id=\"b427a95c-249b-4ecc-8c99-aeabfd9d989a-link\">4<\/a><\/sup> Das f\u00fcr Einsteiger etwas komplexe, aber m\u00e4chtige Syntax kann teilweise durch <em>Nicknames<\/em> sprechender gestaltet werden: <code class=\"\" data-line=\"\">@hourly<\/code> f\u00fcr st\u00fcndlich, <code class=\"\" data-line=\"\">@daily<\/code> f\u00fcr t\u00e4glich usw. Als Alternative dazu bietet Systemd \u00fcbrigens Timer.<sup data-fn=\"c84ebc5b-69bc-4017-a7f0-c05ba1ccbba4\" class=\"fn\"><a href=\"#c84ebc5b-69bc-4017-a7f0-c05ba1ccbba4\" id=\"c84ebc5b-69bc-4017-a7f0-c05ba1ccbba4-link\">5<\/a><\/sup><\/p>\n<p>Relativ unbekannt ist <code class=\"\" data-line=\"\">@reboot<\/code>: Entgegen des Namens f\u00fchrt es Programme nicht nur beim Neustart aus. Sondern auch beim Kaltstart. Im Gegensatz zu den zeitgesteuerten Aufgaben erfolgt dies nur einmal, d.H. es findet keine periodische Wiederholung statt. Ein paar Dinge sind bei der Verwendung von Cron grunds\u00e4tzlich zu beachten:<\/p>\n<ol class=\"wp-block-list\">\n<li>Jeder Benutzer hat seinen eigenen Crontab. Alle darin enthaltenen Skripte werden in seinem Nutzerkontext ausgef\u00fchrt.<\/li>\n<li>Cron verwendet SH als Shell und <code class=\"\" data-line=\"\">$PATH<\/code> steht nicht zur Verf\u00fcgung. F\u00fcr alle Pfade (auch von Bin\u00e4rdateien wie Python, PHP usw) muss der vollst\u00e4ndige Pfad angeben! F\u00fcr Bin\u00e4rdateien k\u00f6nnt ihr daf\u00fcr <code class=\"\" data-line=\"\">type &lt;Befehl&gt;<\/code> (z.B. <code class=\"\" data-line=\"\">type python<\/code>) verwenden.<\/li>\n<\/ol>\n<p>Ruft ihr <code class=\"\" data-line=\"\">crontab -e<\/code> zum ersten Mal mit einem Nutzer auf und habt keinen Standard Text-Editor mit <code class=\"\" data-line=\"\">select-editor<\/code> ausgew\u00e4hlt,  m\u00fcsst ihr zuerst einen festlegen. Vim ist am m\u00e4chtigsten, f\u00fcr Einsteiger ist <code class=\"\" data-line=\"\">nano<\/code> eine weniger m\u00e4chtige, allerdings daf\u00fcr einfach zu bedienende Alternative. Daf\u00fcr gebt ihr die Ziffer &#8222;1&#8220; ein.<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nu-labs@pi5:~ $ select-editor \n\nSelect an editor.  To change later, run &#039;select-editor&#039;.\n  1. \/bin\/nano        &lt;---- easiest\n  2. \/usr\/bin\/vim.basic\n  3. \/usr\/bin\/vim.tiny\n  4. \/bin\/ed\n\nChoose 1-4 &#x5B;1]: n\n<\/pre>\n<\/div>\n<p>Mit dem gew\u00e4hlten Editor \u00f6ffnet Crontab eine tempor\u00e4re Datei, die ihr wie jede andere Textdatei auch damit bearbeiten k\u00f6nnt. Zum starten des Python-Skriptes wird folgende Zeile hinzugef\u00fcgt:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n@reboot \/usr\/bin\/python \/home\/u-labs\/docker-demo\/webserver.py &amp;\n<\/pre>\n<\/div>\n<p>Nach dem Neustart l\u00e4uft der Webserver daher automatisch ohne Zutun auf Port 8080:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nu-labs@pi5:~ $ ps -fC python\nUID          PID    PPID  C STIME TTY          TIME CMD\nu-labs       820       1  0 15:32 ?        00:00:00 \/usr\/bin\/python \/home\/u-labs\/docker-demo\/webserver.py\nu-labs@pi5:~ $ curl http:\/\/127.0.0.1:8080\nHallo vom Python Webserver\n<\/pre>\n<\/div>\n<p>Nachvollziehen kann man das Ausf\u00fchren der Cronjobs auch \u00fcber das Syslog, welches ab Debian (und damit auch Raspberry Pi OS) 12 durch <code class=\"\" data-line=\"\">journalctl<\/code> ersetzt wurde:<sup data-fn=\"bdac9299-8f82-4c81-83c0-42a48865c317\" class=\"fn\"><a href=\"#bdac9299-8f82-4c81-83c0-42a48865c317\" id=\"bdac9299-8f82-4c81-83c0-42a48865c317-link\">6<\/a><\/sup><\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nu-labs@pi5:~ $ journalctl -u cron\n- Boot eecb17a7cf8e4eed84ed6b38b3e26a98 --\nJan 09 15:32:22 pi5 systemd&#x5B;1]: Started cron.service - Regular background program processing daemon.\nJan 09 15:32:22 pi5 cron&#x5B;700]: (CRON) INFO (pidfile fd = 3)\nJan 09 15:32:22 pi5 cron&#x5B;700]: (CRON) INFO (Running @reboot jobs)\nJan 09 15:32:22 pi5 CRON&#x5B;728]: pam_unix(cron:session): session opened for user u-labs(uid=1000) by (uid=0)\nJan 09 15:32:22 pi5 CRON&#x5B;818]: (u-labs) CMD (\/usr\/bin\/python \/home\/u-labs\/docker-demo\/webserver.py &amp;)\n<\/pre>\n<\/div>\n<p>Per <code class=\"\" data-line=\"\">grep<\/code> die Datei <code class=\"\" data-line=\"\">\/var\/log\/syslog<\/code> zu durchsuchen, ist seit Version 12 daher nicht mehr m\u00f6glich.<\/p>\n<h2 class=\"wp-block-heading\">#3 \u00dcber die Desktopumgebung<\/h2>\n<p>Durch die XDG Autostart Spezifikation<sup data-fn=\"7c7c3ef2-c0f5-4259-b81d-2a42dc531d06\" class=\"fn\"><a href=\"#7c7c3ef2-c0f5-4259-b81d-2a42dc531d06\" id=\"7c7c3ef2-c0f5-4259-b81d-2a42dc531d06-link\">7<\/a><\/sup> kann eine <code class=\"\" data-line=\"\">.desktop<\/code> Datei angelegt werden, wenn ihr die Desktop Edition des Raspberry Pi OS installiert habt. Dies macht vor allem f\u00fcr grafische Programme Sinn. Der Name (alles hinter der Endung) ist frei w\u00e4hlbar, ich nenne sie hier im Beispiel <code class=\"\" data-line=\"\">browser.desktop<\/code>:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nmkdir ~\/.config\/autostart\nnano ~\/.config\/autostart\/browser.desktop\n<\/pre>\n<\/div>\n<p>Im Ini-Format l\u00e4sst sich unter <code class=\"\" data-line=\"\">Exec<\/code> der gew\u00fcnschte Befehl angeben. In diesem Beispiel starten wir den Firefox-Browser (seit Raspberry Pi OS 12 vorhanden) und \u00f6ffnen die U-Labs Startseite.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-ini\" data-line=\"\">[Desktop Entry]\nType=Application\nName=Browser starten\nExec=\/usr\/bin\/firefox &quot;https:\/\/u-labs.de&quot;\nTerminal=false<\/code><\/pre>\n<p>Sollte dies bei z.B. eigenen Skripten\/Programmen nicht funktionieren, sehen wir keine Ausgabe m\u00f6glicher Fehlermeldungen. Um das zu \u00e4ndern, installieren wir das grafische Terminal xterm mit <code class=\"\" data-line=\"\">sudo apt install xterm<\/code> und starten es darin. So \u00f6ffnet sich beim Start ein Konsolenfenster mit allen Ausgaben:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-ini\" data-line=\"\">Exec=xterm -hold -e &#039;\/usr\/bin\/python \/home\/u-labs\/script.py&#039;<\/code><\/pre>\n<h2 class=\"wp-block-heading\">So besser nicht: Das \/etc\/rc.local Skript<\/h2>\n<p>Vereinzelt wird noch immer empfohlen, <code class=\"\" data-line=\"\">\/etc\/rc.local<\/code> zu verwenden &#8211; obwohl sie bereits seit Jahrzehnten veraltet ist.<sup data-fn=\"9ab9a849-2c82-40de-a565-0730cc5ff04d\" class=\"fn\"><a href=\"#9ab9a849-2c82-40de-a565-0730cc5ff04d\" id=\"9ab9a849-2c82-40de-a565-0730cc5ff04d-link\">8<\/a><\/sup> In einigen Distributionen ist sie daher nicht vorhanden und wird beim Start nicht aufgerufen. Bei manchen Distributionen funktioniert das dagegen bis heute, weil Debian und Raspberry Pi OS die Abw\u00e4rtskompatibilit\u00e4t mit einem Systemd-Dienst:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nu-labs@pi5:~ $ systemctl status rc-local\n\u25cf rc-local.service - \/etc\/rc.local Compatibility\n     Loaded: loaded (\/lib\/systemd\/system\/rc-local.service; enabled-runtime; preset: enabled)\n    Drop-In: \/usr\/lib\/systemd\/system\/rc-local.service.d\n             \u2514\u2500debian.conf\n             \/etc\/systemd\/system\/rc-local.service.d\n             \u2514\u2500ttyoutput.conf\n     Active: active (exited) since Wed 2024-01-09 16:40:04 CET; 19min ago\n       Docs: man:systemd-rc-local-generator(8)\n    Process: 1300 ExecStart=\/etc\/rc.local start (code=exited, status=0\/SUCCESS)\n<\/pre>\n<\/div>\n<p>Theoretisch w\u00e4re es hier m\u00f6glich, einfach im Skript <code class=\"\" data-line=\"\">\/etc\/rc.local<\/code> \u00fcber <code class=\"\" data-line=\"\">exit 0<\/code> die gew\u00fcnschten Programme\/Skripte einzubinden, damit sie beim Systemstart automatisch aufgerufen werden. Da das Skript bereits seit Jahrzehnten als veraltet markiert ist, steht es unter einigen Distributionen nicht mehr zur Verf\u00fcgung. Ubuntu hat es beispielsweise bereits 2018 in 18.04 LTS entfernt.<sup data-fn=\"725f120b-e683-402a-ac9d-684655809e57\" class=\"fn\"><a href=\"#725f120b-e683-402a-ac9d-684655809e57\" id=\"725f120b-e683-402a-ac9d-684655809e57-link\">9<\/a><\/sup> <\/p>\n<p>Wer die vorherigen Alternativen nicht nutzen m\u00f6chte, dem empfehle ich eine Einarbeitung in Systemd. Einen Anhaltspunkt kann daf\u00fcr ein Blick in den Dienst liefern, der als \u00dcbergangsl\u00f6sung eingerichtet wurde, um <code class=\"\" data-line=\"\">rc.local<\/code> unter Debian\/Raspberry Pi OS nachzur\u00fcsten:<sup data-fn=\"3aabe881-487b-42ff-9f28-bba2e4fad916\" class=\"fn\"><a href=\"#3aabe881-487b-42ff-9f28-bba2e4fad916\" id=\"3aabe881-487b-42ff-9f28-bba2e4fad916-link\">10<\/a><\/sup><\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-ini\" data-line=\"\">u-labs@pi5:~ $ sudo systemctl cat rc-local.service \n# \/lib\/systemd\/system\/rc-local.service\n#  SPDX-License-Identifier: LGPL-2.1-or-later\n#\n#  This file is part of systemd.\n#\n#  systemd is free software; you can redistribute it and\/or modify it\n#  under the terms of the GNU Lesser General Public License as published by\n#  the Free Software Foundation; either version 2.1 of the License, or\n#  (at your option) any later version.\n\n# This unit gets pulled automatically into multi-user.target by\n# systemd-rc-local-generator if \/etc\/rc.local is executable.\n[Unit]\nDescription=\/etc\/rc.local Compatibility\nDocumentation=man:systemd-rc-local-generator(8)\nConditionFileIsExecutable=\/etc\/rc.local\nAfter=network.target\n\n[Service]\nType=forking\nExecStart=\/etc\/rc.local start\nTimeoutSec=0\nRemainAfterExit=yes\nGuessMainPID=no\n\n# \/usr\/lib\/systemd\/system\/rc-local.service.d\/debian.conf\n[Unit]\n# not specified by LSB, but has been behaving that way in Debian under SysV\n# init and upstart\nAfter=network-online.target\n\n# Often contains status messages which users expect to see on the console\n# during boot\n[Service]\nStandardOutput=journal+console\nStandardError=journal+console\n\n# \/etc\/systemd\/system\/rc-local.service.d\/ttyoutput.conf\n[Service]\nStandardOutput=tty<\/code><\/pre>\n<h2 class=\"wp-block-heading\">Quellen<\/h2>\n<ol class=\"wp-block-footnotes\">\n<li id=\"8bfcf139-541c-49bf-b1e7-2e4e4678e14e\">https:\/\/wiki.gentoo.org\/wiki\/Init_system <a href=\"#8bfcf139-541c-49bf-b1e7-2e4e4678e14e-link\" aria-label=\"Zur Fu\u00dfnotenreferenz 1 navigieren\">\u21a9\ufe0e<\/a><\/li>\n<li id=\"deba6ee3-bdf8-4500-b82d-50da47705c8a\">https:\/\/wiki.gentoo.org\/wiki\/Comparison_of_init_systems <a href=\"#deba6ee3-bdf8-4500-b82d-50da47705c8a-link\" aria-label=\"Zur Fu\u00dfnotenreferenz 2 navigieren\">\u21a9\ufe0e<\/a><\/li>\n<li id=\"5f31b5c7-b478-4921-8ff0-c331675e9844\">https:\/\/linux.die.net\/man\/8\/cron <a href=\"#5f31b5c7-b478-4921-8ff0-c331675e9844-link\" aria-label=\"Zur Fu\u00dfnotenreferenz 3 navigieren\">\u21a9\ufe0e<\/a><\/li>\n<li id=\"b427a95c-249b-4ecc-8c99-aeabfd9d989a\">https:\/\/linux.die.net\/man\/5\/crontab <a href=\"#b427a95c-249b-4ecc-8c99-aeabfd9d989a-link\" aria-label=\"Zur Fu\u00dfnotenreferenz 4 navigieren\">\u21a9\ufe0e<\/a><\/li>\n<li id=\"c84ebc5b-69bc-4017-a7f0-c05ba1ccbba4\">https:\/\/www.freedesktop.org\/software\/systemd\/man\/latest\/systemd.timer.html <a href=\"#c84ebc5b-69bc-4017-a7f0-c05ba1ccbba4-link\" aria-label=\"Zur Fu\u00dfnotenreferenz 5 navigieren\">\u21a9\ufe0e<\/a><\/li>\n<li id=\"bdac9299-8f82-4c81-83c0-42a48865c317\">https:\/\/www.thomas-krenn.com\/de\/wiki\/Abl%C3%B6sung_von_\/var\/log\/syslog_durch_journalctl_in_Debian_12 <a href=\"#bdac9299-8f82-4c81-83c0-42a48865c317-link\" aria-label=\"Zur Fu\u00dfnotenreferenz 6 navigieren\">\u21a9\ufe0e<\/a><\/li>\n<li id=\"7c7c3ef2-c0f5-4259-b81d-2a42dc531d06\">https:\/\/wiki.archlinux.org\/title\/XDG_Autostart <a href=\"#7c7c3ef2-c0f5-4259-b81d-2a42dc531d06-link\" aria-label=\"Zur Fu\u00dfnotenreferenz 7 navigieren\">\u21a9\ufe0e<\/a><\/li>\n<li id=\"9ab9a849-2c82-40de-a565-0730cc5ff04d\">https:\/\/unix.stackexchange.com\/a\/471871\/214989 <a href=\"#9ab9a849-2c82-40de-a565-0730cc5ff04d-link\" aria-label=\"Zur Fu\u00dfnotenreferenz 8 navigieren\">\u21a9\ufe0e<\/a><\/li>\n<li id=\"725f120b-e683-402a-ac9d-684655809e57\">https:\/\/wiki.ubuntuusers.de\/Archiv\/rc.local\/ <a href=\"#725f120b-e683-402a-ac9d-684655809e57-link\" aria-label=\"Zur Fu\u00dfnotenreferenz 9 navigieren\">\u21a9\ufe0e<\/a><\/li>\n<li id=\"3aabe881-487b-42ff-9f28-bba2e4fad916\">https:\/\/unix.stackexchange.com\/a\/479766\/214989 <a href=\"#3aabe881-487b-42ff-9f28-bba2e4fad916-link\" aria-label=\"Zur Fu\u00dfnotenreferenz 10 navigieren\">\u21a9\ufe0e<\/a><\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>Bestimmte Programme oder selbst entwickelte Skripte m\u00f6chte man automatisch zusammen mit dem Raspberry Pi starten und ggf. im Hintergrund laufen lassen. Daf\u00fcr muss nicht zwingend ein vergleichsweise komplexes Systemd-Unit entwickelt werden: Ich zeige dir in diesem Beitrag verschiedene Wege, mit und ohne Docker-Container. Au\u00dferdem einen verbotenen, den du besser nicht nutzen solltest, inklusive Erkl\u00e4rung warum. &#8230;<\/p>\n","protected":false},"author":5,"featured_media":12134,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"[{\"content\":\"https:\/\/wiki.gentoo.org\/wiki\/Init_system\",\"id\":\"8bfcf139-541c-49bf-b1e7-2e4e4678e14e\"},{\"content\":\"https:\/\/wiki.gentoo.org\/wiki\/Comparison_of_init_systems\",\"id\":\"deba6ee3-bdf8-4500-b82d-50da47705c8a\"},{\"content\":\"https:\/\/linux.die.net\/man\/8\/cron\",\"id\":\"5f31b5c7-b478-4921-8ff0-c331675e9844\"},{\"content\":\"https:\/\/linux.die.net\/man\/5\/crontab\",\"id\":\"b427a95c-249b-4ecc-8c99-aeabfd9d989a\"},{\"content\":\"https:\/\/www.freedesktop.org\/software\/systemd\/man\/latest\/systemd.timer.html\",\"id\":\"c84ebc5b-69bc-4017-a7f0-c05ba1ccbba4\"},{\"content\":\"https:\/\/www.thomas-krenn.com\/de\/wiki\/Abl%C3%B6sung_von_\/var\/log\/syslog_durch_journalctl_in_Debian_12\",\"id\":\"bdac9299-8f82-4c81-83c0-42a48865c317\"},{\"content\":\"https:\/\/wiki.archlinux.org\/title\/XDG_Autostart\",\"id\":\"7c7c3ef2-c0f5-4259-b81d-2a42dc531d06\"},{\"content\":\"https:\/\/unix.stackexchange.com\/a\/471871\/214989\",\"id\":\"9ab9a849-2c82-40de-a565-0730cc5ff04d\"},{\"content\":\"https:\/\/wiki.ubuntuusers.de\/Archiv\/rc.local\/\",\"id\":\"725f120b-e683-402a-ac9d-684655809e57\"},{\"content\":\"https:\/\/unix.stackexchange.com\/a\/479766\/214989\",\"id\":\"3aabe881-487b-42ff-9f28-bba2e4fad916\"}]"},"categories":[671],"tags":[1101,497,1025],"class_list":["post-12099","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-raspberry-pi","tag-cron","tag-docker","tag-python"],"_links":{"self":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts\/12099","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/comments?post=12099"}],"version-history":[{"count":22,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts\/12099\/revisions"}],"predecessor-version":[{"id":12185,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts\/12099\/revisions\/12185"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/media\/12134"}],"wp:attachment":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/media?parent=12099"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/categories?post=12099"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/tags?post=12099"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}