0

I have written the shell script below for a lab in my college. It must look at a log file being upgraded frequently from another process and create a number of copies passed at invokation. Here is the code (logrotate.sh):

#!/bin/bash

# Usage:
#   logrotate.sh [-n int] [-s signal] logfile
# where:
#   int is an optional integer used to make int number of copies of logfile
#   signal is the name of signal which shell command fuser must send to the process managing logfile
# this script lacks of a strong parameters checking

NCOPIES=4  
LOGSIGNAL=USR1

#use of getopts to parse the arguments  
while getopts "n:s:" OPTION ; do  
    case $OPTION in  
        n) NCOPIES="$OPTARG"  
           ;;  
        s) LOGSIGNAL="$OPTARG"  
           ;;  
        ?) printf "Usage: %s [-n copies to keep] [-s signal to send] filename\n" $(basename $0) >&2  
        exit 1  
           ;;  
    esac  
done  
#shift to read the last parameter (logfile)  
shift $(($OPTIND - 1))  
LOGFILE=$1  

#create logfile.2 logfile.3 ... logfile.NCOPIES  
for i in `seq $NCOPIES -1 1` ; do  
    test -f $LOGFILE.$i && mv $LOGFILE.$i $LOGFILE.$[ $i + 1 ]  
done  

mv $LOGFILE $LOGFILE.1  

#sending signal to process which is writing to logfile to keep on writing to $LOGFILE(original name, without any extensions)  
fuser -k -"$LOGSIGNAL" $LOGFILE.1  

So I wrote two scripts which every second write to the file log:
-the C program (logtest.c):

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

int main()  
{  
    int fd = open("log", O_WRONLY | O_APPEND);  
    if(fd < 0 ){  
        printf("Impossible to open file %s.\n", "log");  
        return -1;  
    }  
    for(;;){  
        if(write(fd, "Ciao bello mio\n", 15) != 15){  
            write(2, "Error in writing data.\n", 23);  
        }  
        sleep(1);  
    }  
    close(fd);  
    exit(0);  
}  

-and the shell script (logtest.sh):

#! /bin/bash  

while true 
do
    echo $(date) >> log
    sleep 1
done  

When I launch

./logtest.sh &
./logrotate.sh log

the script logrotate.sh moves all the files with the correct names (log becomes log.1) and send the signal to the process which owns the file log for that moment (so the shell script logtest.sh) which then keeps on writing on the file log. Furthermore, it seems that there is no difference about which signal I send with fuser: it will react always the same way.

However, if I launch

./logtest &
./logrotate.sh log

it happens that the C program logtest receives the signal from the command fuser and then terminates.

My question is: why the two logging programs have different reactions to the signal sent from fuser? I mean, why the schell script keeps on working, whilst the C program terminates?

In the man page of fuser in the RESTRICTIONS section it says

The -k option only works on processes.

Could it be that shell scripts are not considered as real processes in the shell? This would be new for me... I have searched on Internet but no page found about fuser go deeply inside the signalling section.

4
  • To close voters: This is not OT because the question in a nutshell is: "Why doesn't fuser work with this bash script?"
    – kos
    May 27, 2015 at 12:31
  • Yes, it is. I am sorry kos, but I don't understand what do you mean. May 27, 2015 at 15:35
  • Someone has voted to close this question as off-topic, and therefore this question is going to be reviewed by a number of users; since the question itself is pretty long and someone might be confused by the fact that a C program is mentioned, I just explained in one line why IMO this question is on-topic and is not to be closed
    – kos
    May 27, 2015 at 16:07
  • Yes, in fact my main doubt was about the command fuser, which seemed to me to work in different ways. Thanks to the answers received I solved this problem. May 27, 2015 at 16:19

2 Answers 2

1

Your script logtest.sh does only write into log and closes the file-descriptor immediately. So when you call fuser on log.1 there is no process which has an active file-descriptor for this file.

You can simulate this by running the while loop inside a list

(while true; do echo $(date); sleep 1; done) >> log

And both logtest.sh and logtest.c will terminate no matter which SIGNAL you send because you doesn't handle the signal. With bash this can be done with trap '<COMMAND>' USR1 (take a look at man bash-builtins). But I've no idea how this is done in C (never lerned C).

1
  • In C it works quite the same. The corresponding command for trap is signal(SIGNAL, handler()), where SIGNAL is the signal to handle and handle is a user defined function which accept as parameter an int corresponding to the signal number. Anyway thanks for help! May 27, 2015 at 7:19
0

The problem is that fuser only works on processes currently using a file which have an opened file descriptor for them in the kernel.

While this is true for your C program, this is not true for your bash script:

echo $(date) >> log

Just opens the file, appends stdout to it and immediately closes it. So the file is never deemed as opened by the kernel upon fuser's check.

A simple solution would be to change your bash script so that the file it's kept open until the while loop ends:

#! /bin/bash  

while true 
do
    echo $(date) >> log
    sleep 1
done < log

This way a file descriptor for log is created upon the while loop's start and it's kept open until the while loop's end.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .