Showing posts with label tty. Show all posts
Showing posts with label tty. Show all posts

Sunday, January 5, 2014

Upstart Jobs at login

Login is not the same is startup. Let's just get that out of the way first.
  • Startup is the time between boot and the login screen. It's the habitat of system jobs.
  • Login is the time after you enter your password. It's the habitat of user jobs.

The easy way to run a task at login is to run a script from your .bashrc.
And the (deceptively not-) easy way to run a task at logout is to run a script from your .bash_logout

But today we're not doing it the easy way. Today we're going to use dbus and Upstart.

Emitting Upstart Signals from your .bashrc

It's terribly easy.

1) Emit a user-level Upstart signal by adding a line to .bashrc:

# Upstart signal that .bashrc is running
initctl emit "I_AM_LOGGING_IN"

2) Add a user-level Upstart job to ~.config/upstart/ for one user, or to /usr/share/upstart/sessions/ for all users:

# /home/$USER/.config/upstart/login_test.conf
description "login test"
start on I_AM_LOGGING_IN            # Start criteria
exec /bin/date > /tmp/login_test    # Do something

3) Open a new terminal window (to load the new .bashrc). When you open the window, the Upstart job creates the tempfile at /tmp/login_test.

Clean up: Restore your bashrc, and delete the sample Upstart job.

Can I emit system-level Upstart signals from .bashrc?

Not directly. The script runs as a user, not as root.

You can use a secondary method of triggering system-level Upstart signals, like sending a Dbus signal, or manipulating a file, or connecting to a socket.


Can I emit Upstart signals from .bash_logout?

No.

Using initctl emit in .bash_logout will merely result in an error. The user-level Upstart daemon seems to be terminated before .bash_logout is run. The command will return a cryptic "Rejected send message"error from PID 1 (system Upstart). Since .bash_logout is not running as root, it cannot emit system-level signals.

Also, GUI terminal programs do not not run .bash_logout, unless you specify compatibility (with a flag) when you start.

That easy way of doing login actions is still too hard

Boy, are you difficult to please.

Okay, there is an even easier way, but it's more complicated to explain: Instead of .bashrc emitting an Upstart event, let Upstart listen for a dbus signal.

Here is an example of the dbus message that occurs when I login via SSH to a new session. This signal is emitted by systend-logind every time a new TTY, ssh, or X-based GUI login occurs.

The signal is not emitted when you are in a GUI environment and simply open a terminal window - that's not a login, that's a spawn of your already-existing GUI environment:

signal sender=:1.3 -> dest=(null destination) serial=497 
  path=/org/freedesktop/login1; interface=org.freedesktop.login1.Manager;
  member=SessionNew
    string "4"
    object path "/org/freedesktop/login1/session/_34"


The important elements are the source, the "SessionNew" signal, and the path of the new session.

Aside, let's query systemd-logind to find if the login is to a TTY, X session, or SSH. logind has lots of useful information about each session:

$ dbus-send --system                              \ 
            --dest=org.freedesktop.login1         \
            --print-reply                         \
            --type=method_call                    \
            /org/freedesktop/login1/session/_34   \ # Path from the signal
            org.freedesktop.DBus.Properties.Get   \
            string:org.freedesktop.login1.Session \
            string:Service
method return sender=:1.3 -> dest=:1.211 reply_serial=2
   variant       string "sshd"

It's right. I did connect using ssh.

Now let's construct an Upstart job that runs when I login via a TTY, X Session, or SSH. We will use Upstart's built-in dbus listener.

# /home/$USER/.config/upstart/login_test.conf
description "login test"
start on dbus SIGNAL=SessionNew     # Listen for the dbus Signal
exec /bin/date > /tmp/login_test    # Do something

  • Now, whenever you login to a TTY, X session, or SSH session, the job will run.
  • If your job needs to tell the difference between those sessions, you know how to find out using dbus.
  • If *everybody* needs the job, place it in /usr/share/upstart/sessions/ instead of each user's .config/upstart/


What about super-easy logout jobs?

Logout jobs are harder, and generally not recommended. Not super-easy. They are hard because you can't guarantee they will run. Maybe the user will hold down the power button. Or use the "shutdown -h now" command. Or the power supply sent a message that the battery only has 60 seconds of life left. Or the user absolutely cannot miss that bus....

Here's the dbus signal that systemd-logind emits when a TTY, X, or SSH user session ends:

signal sender=:1.21 -> dest=(null destination) serial=286 
  path=/org/freedesktop/Accounts/User1000; 
  interface=org.freedesktop.Accounts.User; member=Changed

All this tells me is that User1000 now has a different number of sessions running. Maybe it's a login (yes, it emits the same signal upon login). Maybe it's a logout.

Sure, we can do a login-and-logout Upstart job...

# /home/$USER/.config/upstart/login_test.conf
description "login and logout test"
start on dbus SIGNAL=Changed INTERFACE=org.freedesktop.Accounts.User
exec /bin/date > /tmp/login_test

...but then you need logic to figure out who logged in or logged out, and whether it's an event you care about. Certainly doable, but probably not worthwhile for most users.

In other words, if you want to backup-at-logout, you need to structure it as a backup-then-logout sequence. Logout is not an appropriate trigger to start the sequence...from the system's point of view.

But I really want to do a job a logout!

Okay, here's how to do a job when you log out of the GUI environment. Logging out is the trigger. This won't work for SSH or TTY sessions.

The Upstart jobs /etc/init/lightdm.conf and/etc/init/gdm.conf emit a system-level "desktop-shutdown" signal when the X server is stopped. You can use that job as your start criteria.

# /etc/init/logoff_test.conf
description "logout test"
setuid some_username        # Your script probably doesn't need to run as root
start on stopping lightdm   # Run *before* it is stopped
exec /bin/date > /tmp/logoff_test



Saturday, June 2, 2012

How to use a null-modem serial cable

This will be useful when I need access to a headless server (or any other headless machine) with my laptop.

For example, my little mini-ITX headless server needs occasional terminal input, and leaving my big screen and keyboard nearby is taking up valuable space that I prefer to devote to more important issues...like laundry or loose change or perhaps a book or two.


Desktop clutter comparison: extra monitor & keyboard vs serial cable & laptop

Headless Mini-ITX board booted
to Debian 6 (LXDE)
using native keyboard and monitor.
Headless Mini-ITX board booted
to GRUB, seen through serial port.
Laptop acting as terminal



Hardware

The server has a good, old-fashioned RS-232, 9-pin serial port for the console connection. One client also has an RS-232 port. A different client (laptop) has only USB ports.






  • $3.49 for a 6-foot serial null-modem cable, female RS-232 to female RS-232.







  • $3.19 for a 3-foor USB-to-male RS-232 adapter cable. This is for later when I try from my laptop.




  • Connectivity Test of the null-modem cable between two machines with serial ports. This test proves that the cable is really a null-modem cable (not a data cable) and working, that the serial ports are working, and that both systems are configured correctly,  If possible, avoid using the USB-to-serial adapter for this test - it adds another variable unnecessarily.

    Plug the cable into a serial port on each machine. Designate one machine Server and the other machine Client. It really does not matter which.

    On the Server , open the serial port, and leave it open.
    # getty -L ttyS0 115200 vt100

    On the Client with a serial port, install the screen application, then use it at a terminal emulator to connect over the serial port.
    # apt-get install screen
    # screen /dev/ttyS0 115200
      (Hit return once or twice)

    When finished with screen, quit using the command (CTRL+A , then backslash \)

    You should be able to login to the Server from the Client. If you have problems:
    • Make sure you didn't start multiple screen sessions (they interfere)
    • Make sure neither machine is running both getty and screen on the same port (they interfere). One machine should run getty, the other should run screen.
    • Make sure both sides are using the same speed (115200). This modem setup does not auto-negotiate speeds!
    • Try varying which service starts first. After each attempt, kill both the screen and getty services (but only the getty running on that port!)



    Configuring the Server

    If the console is available during a reboot, then you don't need that external monitor or keyboard anymore! A nearby laptop or desktop can serve (this saves me a lot of valuable space). In order to be useful at reboot time, the bootloader and startup need to be configured to use the serial port.

    I'm assuming the first serial port (serial 0 = COM1 = /dev/ttyS0)

    1) Add the serial port to the bootloader.
       If using grub, see the well-written instructions on the Ubuntu Wiki.
       If the bootloader is Syslinux, add the following line to /boot/syslinux.cfg. It must be the first line.
    serial 0 115200

    2) This server runs Debian 6, which uses sysvinit (for newer, upstart-based systems, see these instructions). Edit the /etc/inittab file. Add the following line near Line 55, with the other getty respawn lines:
    T0:2345:respawn:/sbin/getty -L 115200 ttyS0 vt100

    Reload the inittab using the command init q

    3) Edit the /etc/securetty file. In Debian 6, this isn't necessary, the proper ttyS0 entry to create a serial port already exists.

    Instructions
    Even better instructions
    Thorough instructions, including bootloaders
    Syslinux instructions



    Configuring the Client. Not much to do here, since screen is already installed. It works just like the test. If the client does not have a serial port, use a serial-to-USB adapter as shown below.
    # screen /dev/ttyS0 115200      # Null-modem serial-to-serial
    # screen /dev/ttyUSB0 115200    # Using a serial-to-USB adapter

    I found greater success if the server getty is started first, then the client screen. Including when trying to catch the grub boot screen - push the power-on button on the server, then hit [return] on the client to launch screen, then slowly press a few arrow keys and space bar until the menu appears. Sometimes the connection just doesn't work, and after a pause (grub waiting), you hear disk activity (kernel loading). Then just poweroff the server and try again.


    Switching client and server. Console setup is one-way only, because screen and getty cannot control the same port at the same time. If inittab on the server causes getty to respawn, then edit (comment out) and reload inittab to kill that getty.


    Running screen as user instead of root. Screen might refuse to start for a user if the serial port is owned by root. Running as a user is preferable if you'll be using the serial port a lot.

    Create a new group, add the group to the port, change permissions on the port to match the group, and add the user to the group.

    # ls -l /dev/ttyS0
    crw------- 1 root root 4, 64 Jun 1 04:25 /dev/ttyS0
    # groupadd serialport
    # chgrp serialport
    # chmod 0060 /dev/ttys0
    # ls -l /dev/ttyS0
    c---rw---- 1 root serialport 4, 64 Jun 1 04:27 /dev/ttyS0
    # usermod <my-name> --append --groups serialport

    Log out, log back in, now user can access screen.

    $ screen /dev/ttyS0 115200


    Capturing console output to a text file. This is handy for debugging and keeping notes. Screen doesn't really seem to have this capability. But hooray for the shell environment - you can use the 'tee' command. The following will save a textfile that gets overwritten each session, so feel free to change the name each time.

    $ screen /dev/ttyS0 115200 | tee /path/to/console-recording-textfile