This code seems to work. Make sure you understand it all before handing it off as your own. I called the file sigsync.c and hence the program sigsync, hence the chosen name for the environment variable.
Compilation:
gcc -g -O3 -std=gnu11 -Wall -Wextra -Wmissing-prototypes \
-Wstrict-prototypes -Werror sigsync.c -o sigsync
Code:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static int verbose = 0;
static volatile sig_atomic_t sig_num = 0;
static void sigusr1(int signum)
{
sig_num = signum;
}
static void be_childish(const char *file, pid_t parent)
{
char str[512];
FILE *fpr = fopen(file, "r");
if (fpr == NULL)
{
fprintf(stderr, "Failed to open file %s for reading\n", file);
exit(EXIT_FAILURE);
}
while (1)
{
rewind(fpr);
pause();
if (verbose)
printf("Child: got %d\n", sig_num);
while (fscanf(fpr, "%511[^\n]\n", str) == 1)
printf("%s\n", str);
kill(parent, SIGUSR1);
sig_num = 0;
}
/*NOTREACHED*/
fclose(fpr);
}
static void be_parental(const char *file, pid_t child)
{
char str[512];
const char profile[] = "/etc/profile";
FILE *fpr = fopen(profile, "r");
if (fpr == NULL)
{
fprintf(stderr, "Failed to open file %s for reading\n", profile);
exit(EXIT_FAILURE);
}
while (fscanf(fpr, "%511[^\n]\n", str) != EOF)
{
if (strlen(str) > 0)
{
FILE *fpw = fopen(file, "w");
if (fpw == 0)
{
fprintf(stderr, "Failed to open file %s for reading\n", profile);
kill(child, SIGTERM);
exit(EXIT_FAILURE);
}
fprintf(fpw, "%s\n", str);
fclose(fpw);
kill(child, SIGUSR1);
pause();
if (verbose)
printf("Parent: got %d\n", sig_num);
sig_num = 0;
}
}
fclose(fpr);
kill(child, SIGTERM);
}
int main(void)
{
int child;
int parent = getpid();
const char filename[] = "bufor";
/* Make sure file exists and is empty */
FILE *fp = fopen(filename, "w");
if (fp == 0)
{
fprintf(stderr, "Failed to open file %s for writing\n", filename);
exit(EXIT_FAILURE);
}
fclose(fp);
if (getenv("SIGSYNC_VERBOSE") != 0)
verbose = 1;
struct sigaction sa;
sa.sa_handler = sigusr1;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGUSR1, &sa, 0) != 0)
{
fprintf(stderr, "Failed to set signal handler\n");
exit(EXIT_FAILURE);
}
if ((child = fork()) < 0)
{
fprintf(stderr, "Failed to fork\n");
exit(EXIT_FAILURE);
}
else if (child == 0)
be_childish(filename, parent);
else
be_parental(filename, child);
return 0;
}
Sample output:
Note that the leading spaces are completely removed. The code in the parent reading /etc/profile does that, and the reason why is subtle. The "%511[^\n]\n" format does not skip leading white space, but the \n at the end is not treated as 'only match a newline' but as 'match a sequence of white space'. This means it skips over the newline and leading white space on the next line. To preserve the space, use fgets() or getline() instead of fscanf(). Tested on an Ubuntu 14.04 derivative with GCC 5.1.0.
# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...).
umask 027
if [ "$PS1" ]; then
if [ "$BASH" ]; then
PS1='\u@\h:\w\$ '
if [ -f /etc/bash.bashrc ]; then
. /etc/bash.bashrc
fi
else
if [ "`id -u`" -eq 0 ]; then
PS1='# '
else
PS1='$ '
fi
fi
fi
if [ -d /etc/profile.d ]; then
for i in /etc/profile.d/*.sh; do
if [ -r $i ]; then
. $i
fi
done
unset i
fi