Publi

Crea rápidamente servicios con Systemd para iniciar demonios o ejecutar scripts


Hay quien lo odia, hay personas a quienes les gusta y también a quien no le importa. Pero es un sistema que ha entrado en las vidas de muchos de nosotros. Tanto usuarios como sysadmins. Ya que este post va a ser un post pequeño, quiero mostrar un ligero ejemplo paso a paso de la creación de un servicio utilizando systemd. El servicio será sencillo y se limitará a ejecutar un script durante el arranque y el apagado de nuestro sistema, nuestro servidor o nuestro cacharro (si hablamos de IoT).

¿Para qué podemos utilizarlo?

Systemd tiene muchísimas opciones, y esta configuración va a ser muy básica. En principio ejecutar un script de este tipo al arranque puede servir para actualizar automáticamente programas de nuestro servidor, o nuestra Raspberry. Por ejemplo yo lo suelo utilizar para:

  • Poner algún demonio en el arranque. Estos demonios pueden estar hechos en C, en Python, en Java, en Bash o en el lenguaje que queramos. Serán aplicaciones que permanecerán abiertas todo el tiempo para monitorizar o para proporcionar algún servicio.
  • Cargar reglas de iptables, con algún script de ayuda.
  • Actualizar el código de nuestra web desde un repositorio. Vamos, hacer un git pull antes de empezar a servir la web.
  • Enviar una señal para saludar a otros equipos de la red. Logearnos en algún sitio. O enviar nuestra IP externa de un cliente a nuestro servidor para poder ofrecerle mantenimiento.
  • Cargar módulos o realizar configuraciones personalizadas para el sistema. Pueden ser posibles workarounds o chapucillas, carga de módulos, etc.
  • ¿Sugerencias? Dejad un comentario con vuestros servicios personalizados.

Ejecutar scripts al inicio

Imaginemos un script sencillo como este (lo colocamos en /usr/local/bin/mystartup:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash

cd /var/www/mi_proyecto
git pull origin master
STATUS=$?
if (( $STATUS!=0 )); then
   logger "Error al actualizar web"
   exit
fi

npm update
STATUS=$?
if (( $STATUS!=0 )); then
   logger "Error al actualizar npm"
   exit
fi

composer update
STATUS=$?
if (( $STATUS!=0 )); then
   logger "Error al actualizar composer"
   exit
fi

curl -X POST https://axon/machines/webserver -d status=up

Con este script actualizaremos el código de nuestra web (que lo tenemos en un repositorio git), luego actualizaremos los paquetes npm y composer de la misma. Además, tengo un demonio en otro equipo que controla todas las máquinas que tengo conectadas, avisa de posibles errores, da de alta y baja servidores en balanceadores de carga, etc. Es sólo un ejemplo.

A mí también me gusta hacer un script muy sencillo que se ejecuta cuando se apaga o reinicia el ordenador (/usr/local/bin/myshutdown):

1
2
3
4
#!/bin/bash

logger "Shutting down"
curl -X POST https://axon/machines/webserver -d status=down

Con esto, le digo al servidor que controla el estado de mis máquinas que ésta va a ser apagada.

Luego crearemos un archivo en /etc/systemd/system/mystartup.service:

1
2
3
4
5
6
7
8
9
10
11
12
[Unit]
Description=My startup scripts
After=networking.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/mystartup
ExecStop=/usr/local/bin/myshutdown
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

Con este archivo:

  • Establecemos una descripción (Description=) para el servicio.
  • Le decimos que debe ejecutarse después (After=) de la inicialización de la red (networking.target)
  • Es de tipo (Type=) oneshot. Se ejecutan los archivos y se espera a su finalización.
  • ExecStart y ExecEnd especifican los archivos que se ejecutan cuando el servicio se inicia y se detiene respectivamente.
  • RemainAfterExit indica que tras la ejecución del script el servicio se marca como activo.
  • WantedBy nos indica el objetivo que cumple el servicio. Normalmente multi-user.target se ejecuta siempre.

Activar e iniciar

Si no queremos reiniciar el ordenador (cosa que no nos gusta mucho a los usuarios de GNU/Linux, podemos utilizar (podemos utilizar sudo si no nos deja ejecutar los comandos con nuestro usuario actual):

systemctl daemon-reload

Con esto, habremos recargado la configuración del demonio y systemd tiene que haber reconocido nuestro nuevo servicio. Ahora podemos hacer:
systemctl start mystartup.service

O (en modo compatibilidad):
sudo service mystartup start

Una vez iniciado nuestro servicio y viendo que funciona bien podemos hacer lo siguiente:

systemctl enable mystartup.service

Para iniciarlo siempre que iniciemos nuestro sistema. Y, si queremos consultar información sobre el estado del servicio, debemos ejecutar:
systemctl status mystartup.service
● mystartup.service -- My startup scripts
Loaded: loaded (/etc/systemd/system/mystartup.service; disabled; vendor preset: enabled)
Active: active (exited) since dom 2017-11-19 17:58:25 CET; 29min ago
Process: 22085 ExecStart=/usr/local/bin/mystartup (code=exited, status=0/SUCCESS)
Main PID: 22085 (code=exited, status=0/SUCCESS)
nov 19 17:58:25 gaspy-RedDragoN systemd[1]: Starting My startup scripts…
nov 19 17:58:25 gaspy-RedDragoN systemd[1]: Started My startup scripts.

Tipos de servicio

Systemd no está limitado a esto, en los tipos (Type=) de servicio podemos utilizar, además de oneshot:

  • simple: ejecuta el proceso en segundo plano y continúa la inicialización de servicios.
  • forking: éste es para los demonios tradicionales UNIX, esos que se ejecutan en modo demonio. Que utilizan fork() para devolver el control al usuario dejando una versión en segundo plano del programa ejecutándose. Si usamos este modo, es aconsejable utilizar la opción PIDFile= especificando un archivo que almacene la PID del proceso del demonio, ya que systemd no tendrá acceso a ella de otra forma.
  • idle: ejecuta todo cuando no esté ejecutando nada más. Será útil si queremos ver la salida por consola de nuestro demonio o script sin que se mezcle con otros programas que se estén ejecutando.
  • notify: espera a que el servicio envíe una notificación vía sd_notify() es una función de C que encontramos en systemd/sd-daemon.h. Aunque haremos nuestro demonio muy dependiente de systemd.
  • dbus: el servicio figurará como iniciado cuando tenga un identificador dbus propio.

Reiniciar automáticamente el servicio

Al contrario que cuando ejecutamos un script, como en el ejemplo. Un script que tiene un comienzo y su ejecución tiene también un final. Nuestro servicio sea un demonio, es decir, un programa que deba ejecutarse en segundo plano y mantenerse ejecutando todo el tiempo. Por ejemplo, servidores web, websockets, cgi, bases de datos, APIs, servidores de archivos, y cosas así. En ese caso, necesitamos proporcionar un servicio constante en el tiempo. Y durante el tiempo de servicio pueden ocurrir desastres que hagan que nuestro demonio deje de ejecutarse. Estos pueden ser fallos de software, otro proceso lo ha cerrado (SIGINT, SIGTERM, SIGQUIT, SIGKILL, etc), Out Of Memory Kill, etc. En ese tipo de casos podemos estar dejando de proporcionar un servicio esperando que nuestro servidor sea atendido por un administrador que reinicie el servicio.

Es cierto que como administrador nos interesaría saber que ese servicio se ha reiniciado (y de hecho podemos consultar el log del demonio o incluso enviarnos un e-mail cuando eso pase), pero la solución inmediata del problema es reiniciar el servicio. Seguro que habrá veces que reiniciar el servicio no es suficiente, pero por experiencia son las menos. Muchas veces, incluso puede ocurrir una corrupción de memoria en un programa, o un memory leak que haga que nuestro demonio consuma cada vez más memoria y nada más reiniciar vuelve a funcionar. No estoy diciendo que dejemos así nuestros programas, pero a veces nos vemos obligados a hacer chapucillas para asegurar la continuidad del servicio. Otras veces, puede que el programa llegue a un punto que no hemos controlado en su desarrollo o simplemente se ha consumido la memoria del servidor y el kernel ha cerrado el programa. Son casos en los que no podemos permitir una parada, porque estas cosas siempre pasan cuando el sysadmin duerme…

Para eso tenemos la orden Restart= en la que indicamos en qué caso se reiniciará el servicio automáticamente por systemd. Sus posibles valores son:

  • no: No reinicia el servicio bajo ningún concepto.
  • on-success: Reinicia el servicio cuando éste haya terminado con un código de salida de éxito. Normalmente, un programa termina bien cuando su código de salida es 0. Aunque aquí se incluyen algunas señales como SIGHUP o SIGTERM, e incluso podemos utilizar SuccessExitStatus= para especificar qué consideramos una salida exitosa del programa.
  • on-abort: Reinicia el servicio cuando se envíe una señal y no se haya capturado. Por ejemplo, muchos programas no tienen nada programado cuando reciben un SIGUSR1 y por tanto terminan su ejecución de mala manera.
  • on-watchdog: systemd implementa un watchdog. Es decir, un sistema que detendrá nuestro servicio si éste no da señales de vida en un tiempo determinado. El mecanismo que tenemos para dar señales de vida es la función sd_notify() dentro de nuestro demonio. Y el tiempo del que disponemos entre llamada y llamada lo podemos especificar con WatchdogSec=. Si Restart tiene el valor on-watchdog, el servicio no se parará, sino que se reiniciará.
  • on-abnormal: El servicio se reiniciará si se cumple on-abort, on-watchdog o si se sobrepasa el tiempo de espera de arranque o apagado. En systemd podemos especificar el tiempo máximo que el servicio debe tardar en iniciarse (TimeoutStartSec=) o en pararse (TimeoutStopSec=). Los dos juntos se pueden especificar con (TimeoutSec=).
  • on-failure: El servicio se reiniciará si se cumple on-abnormal o el código de salida del programa no es exitoso.
  • always: Reiniciaremos el servicio si se cumple cualquier caso de los anteriores.

Casi siempre podemos utilizar:

Restart=always

Aunque con todas estas opciones podemos afinar un poco la configuración.

También es importante indicar que RestartSec= nos permite especificar el tiempo de espera antes de reiniciar. Además, puede darse el caso de un servicio que se reinicie todo el rato, por lo que podemos configurar StartLimitBurst= con el número de veces que se reiniciará el servicio y StartLimitIntervalSec= con el tiempo máximo en el que podemos reiniciar ese número de veces. Por tanto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit]
Description=My startup scripts
After=networking.target

[Service]
Type=simple
ExecStart=/usr/local/bin/mydaemon
Restart=always
RestartSec=10
StartLimitBurst=5
StartLimitInterval=600

[Install]
WantedBy=multi-user.target

En este caso, si el servicio, durante 10 minutos (600 segundos), sólo se podrá reiniciar 5 veces (esperando 10 segundos antes de reiniciarse cada vez). Y si el servicio en esos 10 minutos no termina de arrancar, systemd desistirá y no lo intentará más hasta dentro de 10 minutos.

Por poder, podemos poner un script que cada diez minutos se reinicie dos veces y se pare. Como no es un demonio y va a finalizar siempre estaremos siempre reiniciándolo. Aunque para eso podemos utilizar por ejemplo cron, o hacer que nuestro script tenga esperas (sleep) con el fin de no recargar constantemente el programa, ya que todo esto tiene un coste para el sistema operativo.

Tareas previas y posteriores

Podemos, además, ejecutar programas antes y después de iniciar servicio con ExecStartPre=, ExecStartPost= que podemos poner todas las veces que queramos para ejecutar todos los programas o scripts que sean necesarios antes de iniciar el servicio y una vez éste esté iniciado.

También disponemos de Use ExecStopPost= para ejecutar comandos una vez se haya detenido el servicio.

Más información

Lo mejor para completar la información es ver el manual. Aquí vemos dos páginas que nos ayudarán:

Foto principal: Denys Nevozhai

También podría interesarte....

There are 34 comments left Ir a comentario

  1. Pingback: Crea rápidamente servicios con Systemd para iniciar demonios o ejecutar scripts | PlanetaLibre /

  2. 5duros /
    Usando Google Chrome Google Chrome 62.0.3202.94 en Windows Windows 7

    Buenas tardes,en mi tesis estoy utilizando la creación de servicios, para tal he creado un servicio que ejecute un comando, en concreto es este ikec -r «configuración» -u usuario -p contraseña -a , el cual establece una conexión VPN, utilizando el software «shrew software vpn», pero en el log al iniciar el sistema, me dice que falla a la hora de cargar el archivo de configuración. Pero cuando ya ha cargado todo el SO y desde la interfaz gráfica ejecuto un shell y reinicio este servicio este empieza a funcionar correctamente.
    Por lo que mi pregunta es, como puedo iniciar este servicio despues de X tiempo (como si del cron se tratase, pero sin utilizar el cron) o que se ejecute cuando ya se ha cargado todo el SO?
    El archivo de mi servicio contiene esto:

    [Unit]
    Description= Custom automatic vpn configuration

    [Service]
    Type=simple
    ExecStart=/usr/local/bin/custom_vpn_start.sh
    Restart=always

    [Install]
    WantedBy=default.target

    1. Gaspar Fernández / Post Author
      Usando Mozilla Firefox Mozilla Firefox 57.0 en Ubuntu Linux Ubuntu Linux

      Podrías incluir algo dentro de [Service]

      1
      2
      [Service]
      ExecStartPre=/bin/sleep 30

      Con esto te aseguras de ejecutar sleep antes de cargar el servicio. Aunque no es lo más bonito, también podrías ver si necesitas que un servicio se cargue antes que el tuyo, y dentro de
      [Unit] colocar la configuración:

      1
      2
      [Unit]
       After=servicio
  3. Oscar /
    Usando Mozilla Firefox Mozilla Firefox 57.0 en Fedora Linux Fedora Linux

    Ojo, en https://www.freedesktop.org/software/systemd/man/systemd.unit.html#StartLimitIntervalSec= se dice esto respecto qué pasa cuando ha transcurrido el tiempo indicado en StartLimitIntervalSec y creo que no cuadra con lo comentado en el artículo:

    «Note that units which are configured for Restart= and which reach the start limit are not attempted to be restarted anymore; however, they may still be restarted manually at a later point, after the interval has passed. From this point on, the restart logic is activated again.»

    1. Gaspar Fernández / Post Author
      Usando Mozilla Firefox Mozilla Firefox 57.0 en Ubuntu Linux Ubuntu Linux

      Ahí dice que las unidades configuradas para reiniciarse con Restart= que alcancen el StartLimitIntervalSec= no se van a reiniciar más (automáticamente). Las podremos reiniciar a mano si queremos luego y se repetirá de nuevo la lógica de reinicios. En el post digo que StartLimitIntervalSec es el tiempo máximo para hacer todos los reinicios del StartLimitBurst.

      Lo siento, tal vez no me expliqué bien, le daré una vuelta al post.

      Gracias por tu comentario.

  4. truko22 /
    Usando Mozilla Firefox Mozilla Firefox 57.0 en Linux Linux

    Excelente 😀

    1. Gaspar Fernández / Post Author
      Usando Mozilla Firefox Mozilla Firefox 57.0 en Linux Linux

      Gracias!

  5. dani /
    Usando Mozilla Firefox Mozilla Firefox 58.0 en Ubuntu Linux Ubuntu Linux

    Hola,
    quiero hacer un demonio que se inicie cuando se conecte/desconecte un periférico. ¿Es esto posible o por el contrario tengo que buscar otro modo de hacerlo?

    1. Gaspar Fernández / Post Author
      Usando Mozilla Firefox Mozilla Firefox 57.0 en Linux Linux

      Para eso tienes que atacar a udev también. Si quieres echale un ojo a esto: https://unix.stackexchange.com/questions/28548/how-to-run-custom-scripts-upon-usb-device-plug-in
      Pero como quieres un demonio, en lugar de RUN, puedes poner ENV{SYSTEMD_WANTS}=»midemonio.service»

      Ya me dices si lo logras.

      1. Dani /
        Usando Mozilla Firefox Mozilla Firefox 58.0 en Ubuntu Linux Ubuntu Linux

        Hola muchas gracias por la respuesta. Mi problema es que tengo unos cascos inalámbricos que consta de 2 componentes. Uno es el aparato de bluetooth que esta siempre conectado y los cascos que los detecta el SO cuando lo enciendo. Pero cuando lo apago al tener el dispositivo bluetooth conectado pulse me lo sigue detectando y no se desactivan los cascos. Lo que quiero hacer es que cuando apague los cascos ejecute un script y me lo desactive y al contrario al encenderlo. Pensaba que iba a necesitar un demonio para esto, pero al ver lo que acabas de compartirme creo que es la solución que necesito. Muchas gracias de nuevo, me has salvado.

  6. Toño /
    Usando Mozilla Firefox Mozilla Firefox 58.0 en Ubuntu Linux Ubuntu Linux

    Excelente explicación.
    Gracias.

    1. Gaspar Fernández / Post Author
      Usando Mozilla Firefox Mozilla Firefox 61.0 en Ubuntu Linux Ubuntu Linux

      Muchas gracias Toño!!

  7. Ángel /
    Usando Mozilla Firefox Mozilla Firefox 68.0 en Mac OS X Mac OS X 10

    Esto valdría para cargar un script de bash de xrandr, para establecer una determinada resolución de pantalla desde el arranque, ¿no?

    Gracias, fantástico como siempre.

    1. Gaspar Fernández / Post Author
      Usando Google Chrome Google Chrome 87.0.4280.88 en Linux Linux

      Podrías hacerlo de esta forma, o también usar scripts al inicio del servidor X, con scripts de tu gestor de ventanas. Más que nada porque systemd va a ejecutar lo que le digas pase lo que pase, incluso si el servidor X ha tenido un problema. Pero queremos que se ejecute xrandr cuando el servidor X esté ejecutándose ya.

  8. Xavi Gorriz /
    Usando Google Chrome Google Chrome 78.0.3904.108 en Windows Windows 7

    Buenas! Estoy migrando de un arranque de un script perl por initd a systemd …anteriormente, al ejecutarse, en la consola, en el prompt del login, se podia ver la salida del script perl … ahora con el systemd no aparece nada… puedo ver el log con el comando , pero no queda nada elegante …se visualiza en modo log con indicaciones de tiempo etc…
    hay alguna forma de enrutar la salida del demonio systmd hacia la consola ??
    saludos!

    1. Gaspar Fernández / Post Author
      Usando Google Chrome Google Chrome 87.0.4280.88 en Linux Linux

      Hola Xavi,
      Como estamos hablando de demonios, no tiene mucho sentido que haya cosas que se vean por la consola normal. Normalmente se lanza el demonio y está en segundo plano, y te tiene que dejar seguir trabajando.
      En cualquier caso, no sé si te vendrá bien, lo que puedes hacer es redirigir la salida estándar o de error a un fichero. Dentro de [Service] puedes incluir:
      StandardOutput=/var/log/salida_demonio.log
      StandardError=/var/log/error_demonio.log

      Y puede hacer
      $ tail -f /var/log/salida_demonio.log
      $ tail -f /var/log/error_demonio.log

      Por ejemplo,
      tail -f permanece en modo «interactivo» más o menos, hasta que haces Control+C y va poniendo en pantalla los cambios en el archivo.

      Puedes probar también redirigir las salidas a named pipes ( https://poesiabinaria.net/2009/07/tuberias-con-nombre-para-comunicacion-entre-procesos/ ) o a syslog (aunque en syslog tendrás también marcas de tiempo).

      Saludos

  9. Luis Angel Gutierrez /
    Usando Google Chrome Google Chrome 86.0.4240.197 en Linux Linux

    Hola Gaspar,

    He seguido tu tutorial, que está muy bien explicado, cosa que te agradezco. Crear un servicio sencillo, por ejemplo que se ejecute un script en el arranque, no es complicado, incluso para alguien como yo que no tiene muchos conocimientos de linux. Sin embargo no acabo de lograr hacer lo que quiero. A ver si me puedes orientar.
    Utilizo una RPI4 como servidor, con Raspberry Pi OS lite, funcionando de continuo. En ella tengo corriendo algún contenedor. Por otro lado, la quiero utilizar también como kiosko, de manera que, cuando yo quiera, pongo en marcha el kiosko, incluso al arrancar la propia RPI. Para ello estoy intentando crear un servicio, que lo único que hace es ejecutar un srcipt en bash, que muestra páginas web utilizando chromium. El servicio es este:

    [Unit]
    Description=Chromium Kiosk
    Wants=graphical.target
    After=graphical.target

    [Service]
    Environment=DISPLAY=:0.0
    Environment=XAUTHORITY=/home/pi/.Xauthority
    Type=simple
    ExecStart=/bin/bash /home/pi/kiosk.sh
    Restart=on-abort
    User=pi
    Group=pi

    [Install]
    WantedBy=graphical.target

    Lógicamente, para poder visualizar chromium tengo que tener un entorno gráfico. Tengo instalado Xorg. Mi duda es dónde pongo la orden de arrancar este entorno gráfico utilizando «startx». Entiendo que sería lo primero que tengo que hacer al iniciar el servicio.
    Quiero que sea dentro del propio servicio, ya que así, cuando lo pare, se me cerrará también el entorno gráfico, ocupando menos RAM.

    A ver si me puedes decir algo.

    Gracias.

    Saludos
    Luis Angel Gutiérrez

    1. Gaspar Fernández / Post Author
      Usando Google Chrome Google Chrome 87.0.4280.88 en Linux Linux

      Hola Luis Angel,

      Si quieres, puedes tirar por algo más sencillo. Si tu entorno de escritorio para el modo kiosko sigue las especificaciones freedesktop (los entornos principales y más grandes lo siguen, si no, prueba). Puedes añadir un enlace simbólico en ~/.config/autostart-scripts para que se inicie automáticamente cuando arranque el entorno gráfico y entres en la sesión.
      O como es modo kiosko solamente, puedes intentarlo por ~/.xinitrc si no puedes con el método anterior.

      Sobre todo porque haciéndolo desde systemd tendremos que esperar a que esté todo iniciado y puedes tener problemas con la XAuthority. Por cierto, es mejor que pongas las variables de entorno entre comillas:

      Environment=»DISPLAY=:0.0″

      Por lo que pueda pasar, siempre se nos puede colar alguna cosa que no le guste al parseador de configuración.

      Un saludo!

  10. Dan P. /
    Usando Mozilla Firefox Mozilla Firefox 87.0 en Mac OS X Mac OS X 10

    Hola.
    Soy un amateur total en cuanto a programación. Muy poco de lo que se lo aprendí gracias a mi raspberry. MI pregunta es la siguiente: ¿Que no va bien en mi script (https://pastebin.com/1LEAY70r) para que me reinicie el servicio de sickchill? Si pudieras echarme un cable, sería maravilloso y te lo agradeceria. (he notado que me faltaba un «;» despues del corchete. lo añadi, pero sigue sin funcionar.
    UN saludo, y muchas gracias.

    1. Gaspar Fernández / Post Author
      Usando Google Chrome Google Chrome 87.0.4280.88 en Linux Linux

      Hola Dan P.
      En principio el script debería funcionar, ¿qué fallo te da? Lo mismo el problema está en otro sitio. ¿Lo llamas desde un cron? Lo mismo sudo se queda pidiendo contraseña en una sesión que no es interactiva.

      Saludos

  11. Josh /
    Usando Google Chrome Google Chrome 89.0.4389.128 en Windows Windows NT

    Muchas gracias logre iniciar un daemon con un servicio con su tutorial 😀

    1. Gaspar Fernández / Post Author
      Usando Google Chrome Google Chrome 87.0.4280.88 en Linux Linux

      ¡¡Gracias a ti Josh!! Me alegro un montón de que hayas podido cumplir tu objetivo.

  12. jimmy johnshon /
    Usando Google Chrome Google Chrome 117.0.0.0 en Windows Windows NT

    A well-known word-puzzle game that was mentioned in The New York Times is referred to as Wordle NYT. In the straightforward word-guessing game Wordle, players have six chances to correctly identify a five-letter word. The game gives feedback after each guess by highlighting the guessed word’s letters in various colours to show whether they are in the target word and whether they are in the right place. Players can improve their predictions and finally solve the problem by using this feedback.
    visit: https://wordleplay.info/

  13. SPORTS BETTING /
    Usando Google Chrome Google Chrome 117.0.0.0 en Windows Windows NT

    There’s no doubt i would fully rate it after i read what is the idea about this article. You did a nice job.
    rugby world cup odds

  14. OKBet /
    Usando Google Chrome Google Chrome 118.0.0.0 en Windows Windows NT

    I found this post very interesting and informative. Thank you for sharing your special thoughts with us. My site: King of Glory

  15. Ainz /
    Usando Google Chrome Google Chrome 119.0.0.0 en Windows Windows NT

    http://www.drywallmarietta.com is looking forward to more insights and personalized services from the community!

  16. ufa168 /
    Usando Google Chrome Google Chrome 107.0.0.0 en Windows Windows NT

    I wanted to thank you for this great read!! I definitely enjoying every little bit of it I have you bookmarked to check out new stuff you post. ufa168

  17. Angel17 /
    Usando Google Chrome Google Chrome 119.0.0.0 en Windows Windows NT

    Cool. Thanks for sharing this. eco-55 pellet stove

  18. CiVault /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    A very simple script but interesting actually in this article all the info is in here you only know if you start reading and understand the run script is already detailed and the tutorial shows you how to configure system services automatically.
    Google Maps Citations

  19. subhanakhtar /
    Usando Google Chrome Google Chrome 121.0.0.0 en Windows Windows NT

    Wordle on NYTimes combines intellect and entertainment in a unique word-guessing experience.
    Wordle nyt

  20. connections nyt /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Connections NYT is a captivating and intellectually stimulating word puzzle game published in The New York Times. It challenges players to forge links between words using shared associations, creating a network of interconnected terms. This unique game has gained popularity for its engaging gameplay and the mental agility it requires.

  21. Nytimes wordle /
    Usando Google Chrome Google Chrome 122.0.0.0 en Windows Windows NT

    Wordle’s addictive gameplay and daily updates ensure that players always have a reason to come back for more.

  22. Bruno /
    Usando Google Chrome Google Chrome 122.0.0.0 en Windows Windows NT

    Systemd has become integral for many users and sysadmins. It provides a straightforward means to execute scripts during system startup and shutdown.

  23. Beauty /
    Usando Google Chrome Google Chrome 122.0.0.0 en Windows Windows NT

    I do elieve all the concepts you’ve introduced on yohr post. They’re really convincing and can definitely work.

    Nonetheless, the posts are too quicck for newbies. May you please extend themm a bit from next time? Thank you for the post.

Leave a Reply to Oscar Cancle Reply