Running a Ruby script once at Linux login

2

I have a Ruby script which takes quite a while to run (5-20 seconds in most cases) and its purpose is to generate configuration files for Conky and Fluxbox.

At the moment I have the Ruby script set to run during the Fluxbox startup via adding it to the ~/.fluxbox/startup file, but this causes a delay in Fluxbox starting since the config files has to be written before I can allow Fluxbox to start.

The way I usually use my laptop is to login to the terminal shell (bash) only running startx to get a graphical environment when I need it. In doing so I was looking for a way to have my script run at login in the background, but only run once. This means for any further shells spawned it will not run my script. Furthermore I need this to run only for a specific user when they log in.

What are my options? My script allows for editing of specific users config files so I can probably run my script at boot via the root user somehow (Ex: An init script set at the default run level... If I can get Ruby to work with init without stalling the init sequence like it does fluxbox, or maybe rc.local?). Otherwise is there a way to make the script run once, only at the initial login for a specific user?

Any help would be appreciated.

user613083

Posted 2016-07-03T17:51:50.463

Reputation: 21

Answers

2

Sounds like it needs to run when your system gets to runlevel 3. Try moving your script to /etc/rc3.d/S50scriptname

glenn jackman

Posted 2016-07-03T17:51:50.463

Reputation: 18 546

I was thinking the same, but will this halt the init process once it enters runlevel 3 for the length of the script, or can call my script in the background via &. I am basically looking for a way to run it in the background only once either on system boot, or user login. – user613083 – 2016-07-03T18:54:24.673

+1 Your approach is different than mine, yet I think it is useful. Either one has its own advantages. – Kamil Maciorowski – 2016-07-03T20:08:58.747

1

My general idea:

Run the script from .bashrc in background:

/foo/bar/script &

or maybe a better alternative:

nohup /foo/bar/script &

The script should check if another instance of it has been started, it should silently exit if so. I don't know Ruby but it should be possible (in case it's not: build "wrapper" script in Bash that does the checking and runs your Ruby script or not). General ways may be: ps-like query, or lock file dir (see this) in /dev/shm or /run/shm (/dev/shm is in memory and will be cleared by the next restart – perfect). Otherwise the script should:

  • (ps-like query method) do its job and never exit (a low cost infinite waiting loop of some sort?);
  • (lock dir method) create lock dir, do its job and exit.

This way it will run only once.

If you choose ps-like query method, then keep in mind that your script will be killed at logout, unless you use nohup.

Edit:

In a general multiuser environment some rouge user A may block user B's script by creating the lock dir. You may create lock dir inside ~/ to avoid this scenario, but then you should delete it at the right time. The ps solution shouldn't be prone to this problem.


The solution with /etc/rc3.d (glenn jackman's answer) may be fine. Its advantage: it will run once out of the box. There are differences between the two solutions and you should know about it (not all of them will be relevant to you in particular, but in general case):

  1. .bashrc runs as particular user (normally: non-root); scripts under rc3.d will run as root which is potentially more dangerous in case of a mishap.
  2. You don't need root access to implement .bashrc solution (although your script may need it).
  3. Multiple users may implement .bashrc solutions and it will not overload your system unless they login simultaneously.
  4. In my opinion it's the right thing to implement user-specific solutions at user level (.bashrc), not the system level (rc3.d).

Kamil Maciorowski

Posted 2016-07-03T17:51:50.463

Reputation: 38 429

Doesn't .bashrc get sourced each time a new shell is loaded? I am looking for a way in particular so that my script is ran in the background only once either at boot, or user login. The script itself only has a life time of 5-20 seconds. A lock file may work I will have to try it out. Do you have an example of using /dev/shm? – user613083 – 2016-07-03T18:59:22.557

@user613083 Yes, it does; that's why we should check if the script has already been started. The example is a first codeblock in the answer linked. It uses /tmp which may or may not be in memory. Replace it with location in memory like /dev/shm.

– Kamil Maciorowski – 2016-07-03T19:06:25.527

Can I simply call mkdir on a location inside /dev/shm? Such as mkdir /dev/shm/myscript.lock ? I've never seen this device node before. – user613083 – 2016-07-03T19:19:10.743

I can in my Ubuntu, in my Debian – as a regular user. Try it and you will know. This directory should be writable for all and have sticky bit set. Read about sticky bit here ("sticky bit" section). In my Ubuntu /dev/shm is not a device node but a symlink to /run/shm which is a mountpoint of tmpfs filesystem – hence: in memory. Well, maybe I should use /run/shm in the first place; I just got used to /dev/shm.

– Kamil Maciorowski – 2016-07-03T19:28:42.953

@user613083 I have edited (expanded) my answer. You may find the new fragment useful. – Kamil Maciorowski – 2016-07-03T20:10:11.097