Thursday, January 5, 2012

Using kill

Versión en español

While working, I have heard persons saying that the terminate the process / services using kill -9, because that way "the process always ends" (true history). This way of terminating process has a problem, you can lose information.

Let's explain it. First, the kill command , this command sends signals to the processes (see wikipedia). This type of interprocess communication is very simple and asynchronous (usually). If you already checked the man pages (man 7 signal) or the wikipedia link, you will see that there are different signals, for example:: SIGINT, SIGTERM, SIGKILL, etc; and there are some signals that can't be handled, for example: SIGKILL. ¿What does it mean that it can't be handled? According mi experience, you can't program a handler for those signals, in other words the process never see it coming, the os just apply the signal..

For a better understanding, let's use our best friend C (I hope you have the C compiler installed)

First, create a file called test.c

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

volatile int FINISH = 0;

void handler(int sig)
{
    if (sig == SIGINT) {
        fprintf(stderr, "Interrupted: Closing files\n");
        FINISH = 1;
    } else if (sig == SIGTERM) {
        fprintf(stderr, "Terminating: Closing files\n");
        FINISH = 1;
    } else {
        fprintf(stderr, "I don't know what to do with this signal\n");
    }
}

int main(void)
{
    FILE *f = NULL;
    struct sigaction sa;
    sigemptyset(&sa.sa_mask);
    sa.sa_handler = handler;
    if (sigaction(SIGINT, &sa, NULL) == -1) {
        perror("sigaction with SIGINT");
    }
    if (sigaction(SIGTERM, &sa, NULL) == -1) {
        perror("sigaction with SIGTERM");
    }
    if ((f = fopen("test_file.txt", "w")) == NULL) {
        perror("Couldn't open test_file.txt for writing");
        exit(1);
    }
    while (!FINISH) {
        fprintf(f, "hello");
        fprintf(f, " ");
        sleep(5);
        fprintf(f, "world\n");
    }
    fclose(f);
    exit(0);
}

Some remarks, the handler function handles the signals defined with sigaction. I didn't try to handle  SIGKILL a.k.a 9, because you can't handle it, if you try it, the os will ignore you. The code is in a while loop writing hello world in a file. When we send the interrupt or termination signal, the program will finish gracefully, closing the file, etc. Instead if we send the SIGKILL, it's very likely that the file ends empty or corrupt. Try it.

Compile
$ cc -Wall -o test test.c

Execute
$ ./test

In another terminal, execute some of this three commands
pkill -SIGINT test
pkill -SIGTERM test
pkill -SIGKILL test

Between executions, check the test_file.txt file and compare the results. If you exec kill with SIGKILL the results should not be as expected. Now you know why, you shouldn't terminate processes, like a database system with SIGKILL. You could end with lost or corrupt data (a lot of fun ^_^).

As a remainder, first try to terminate the process being nice (SIGTERM), and if after a time it doesn't end, then you call your hitman friend (the os) to do the dirty job (SIGKILL).

That's all folks.

Alejandro

Monday, March 7, 2011

Running programs without a chroot

Para versión en español


Warning: This information may not work for you. If while following this instructions, your machine explodes or does not work as it should, is your only responsibility. You should first tested it in a test system and not in a production system. I hope you have a backup. It is not my fault (^_^).

Some time ago, I had to run an 32-bits program in a 64-bits system. Usually you would use a chroot. But this 32-bits program must be ran by another program, this other program does not have root privileges (you should not be root without reason, be safe kid (^_^) ). This impedes the use of chroot (you need root privileges to run chroot), plus I did not want to wrestle with library conflicts (it can be bad for health).

After some googling and man, I found the about ld.so. I only had to set the libraries to use for the program. First set environmental variable LD_LIBRARY_PATH with the libraries path. Sometimes you only need to do this. It did not work for me (;_;) . I got a lot of errors like symbol problems, etc. So I kept reading about ld.so and found a solution. The system was using its loader, not exactly what I needed. The choice besides recompiling, was to call the specific loader. To know which loader to use, first check with: ldd <absolute program path> . Search the line ld-linux, grep is your friend, then you get  ld-linux<something>.so (symbolic link to ld-<version>.so) your program uses.

An example

In a 64-bits system with a 32-bits program

With the folder structure






Running
$ ldd /home/<user>/<program_folder>/bin/<program> | grep ld-linux


You get (this file needs to be copied from the original system to the system where you plan to run the program)
/lib/ld-linux.so.2


Copy this loader to
/home/<user>/<program_folder>/lib/ld-linux.so.2

To run the program
$ env LD_LIBRARY_PATH=/home/<user>/<program_folder>/lib/:$LD_LIBRARY_PATH \
/home/<user>/<program_folder>/lib/ld-linux.so.2 \
/home/<user>/<program_folder>/bin/<program> <arguments>*


A little messy, but you can make a script. You may ask if you have any question


Alejandro