I'm in the proccess of writing a deamon, which runs a shell-skript or any other executable upon a buzzer-press. Most of what I need works quite fine, but I'm facing a curious issue:
I do the call of the executable via execlp. And, when I do have STDIN and STDOUT open, this works just beautifully.
However, as this is supposed to be handled completely deamonized, thus in the background, I would like to run this without any console-interface. But, as soon as I close the standard interface identifiers, execlp does not work anymore.
I tried it with the snippet below: When it is run with a d as argument for debug, the identifiers will be kept open and it works. Otherwise, it does not. Thus, the question is: How to run an executable (by any means), without using STDIN and STDOUT.
Some remarks:
The call to execlp does not return. Which means, that the syslog-call is never done and which is as expected.
When leaving STDERR open, nothing comes up.
The script to be called returns with an error-code (consciously for debugging reasons).
When executing with file-handlers open, I do see the toggling of the pins and the wait-call returns the error-code of the sript.
When executing without the file-handlers, the wait call returns the exit-status success.
Note, that the c call to execlp does not return. Thus, inside this little programm, it looks like success.
Here is the c-code:
/** Global Includes: ****************************************************************/
#include <stdio.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/wait.h>
/** Main-Function: ******************************************************************/
int main (int argc, char **argv) {
/** Variables: */
pid_t pid, sid;
/** Set up the demon: ***********************************************************/
pid = fork();
if (pid < 0) {
return -2;
}
/** If we got a good PID, then we can exit the parent process: */
if (pid > 0) {
printf("Sturting Deamon as PID %i.\n", pid);
int iStatus;
pid_t pid;
pid = waitpid(-1, &iStatus, 0);
if ((pid == 0) || (pid == -1)) {
printf("Client exited unknown!\n");
}else{
if (WEXITSTATUS(iStatus) == 0) {
printf("Client %i exited success!\n", pid);
}else{
printf("Client %i exited with error!\n", pid);
}
}
return 0;
}
/** Close out the standard file descriptors: */
if ((argv[1][0]) != 'd') {
close(STDIN_FILENO);
close(STDOUT_FILENO);
//close(STDERR_FILENO);
}
/** Open syslog: */
openlog( "BuzzerD", LOG_PID | LOG_CONS | LOG_NDELAY, LOG_LOCAL0 );
/** Try to run: */
execlp("bash", "bash", "/home/pi/TexPrint", "/home/pi/SoLieb.tex", NULL);
syslog(LOG_ERR | LOG_DAEMON, "FAILURE running execlp!");
_exit(1);
}
Here is the sript to be called:
#SOURCE=$1
#TARGET=${SOURCE/tex/pdf}
#echo Compiling $SOURCE to $TARGET and print!
#echo pdflatex $SOURCE
#echo Trying to print $TARGET ...
#echo lp -d Brother_MFC_J5930DW $TARGET
#echo .
/home/pi/BuzzerD/buzzerd -l off
sleep 3
/home/pi/BuzzerD/buzzerd -l alive
exit 1
Related
I read about APUE 3rd, 8.13, system Function, and I saw a version of system function implementation without signal handling.Code is like below:
#include <sys/wait.h>
#include <errno.h>
#include <unistd.h>
int system(const char *cmdstring) /* version without signal handling */
{
pid_t pid;
int status;
if (cmdstring == NULL)
return(1); /* always a command processor with UNIX */
if ((pid = fork()) < 0) {
status = -1; /* probably out of processes */
} else if (pid == 0) { /* child */
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
_exit(127); /* execl error */
} else { /* parent */
while (waitpid(pid, &status, 0) < 0) {
if (errno != EINTR) {
status = -1; /* error other than EINTR from waitpid() */
break;
}
}
}
return(status);
}
And the code used to test system function of this version is like below:
int main(void)
{
int status;
if ((status = system("date")) < 0)
err_sys("system() error");
pr_exit(status);
if ((status = system("nosuchcommand")) < 0)
err_sys("system() error");
pr_exit(status);
if ((status = system("who; exit 44")) < 0)
err_sys("system() error");
pr_exit(status);
exit(0);
}
And the result of the test code is shown by the picture(just ignore the Chinese in the result if you can't understand):
I wonder why will execl return if "nosuchcommand", which is not valid for /bin/sh, is given to /bin/sh. In my point of view, execl just replace the code of current process and then run from entry point, even though "nosuchcommand" is not valid for /bin/sh, it has nothing to do with execl but /bin/sh. So, how execl know "nosuchcommand" is not valid for /bin/sh to execute and return? Does execl treat /bin/sh differently by checking the command given to /bin/sh before executing /bin/sh so that it will know the invalid argument given to /bin/sh in advance? I know execl won't treat /bin/sh differently, so, how does execl know "nosuchcommand" is not valid for /bin/sh to execute and return?
sh -c nosuchcommand itself returns 127. It's one of those return codes with a special meaning.
So I don't think you're seeing execl actually returning in this case.
It doesn't "know". It simply executes what you tell it to. /bin/sh then reports that it cannot find it, after which /bin/sh exits with non-zero exit code, in this case 127.
Also note that you cannot depend on it returning exactly 127 as that is shell-specific. Some shells (including /bin/sh on some OSes) will return 1 instead.
I've got a simple text file called "tmp" under current directory, I wish to "cat" this file and then "sort" it, I want to use a c program to act like pipe "|" so I tried to use a father/child talk to do this.
Unexpectedly, the program hangs after "cat", like below:
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
int main(){
int pipefd[2];
pipe(pipefd);
int& readfd=pipefd[0];
int& writefd=pipefd[1];
pid_t pid=fork();
if(pid==0){//child
dup2(STDIN_FILENO,writefd);
close(readfd);
execlp("cat","cat","tmp",NULL);
printf("child cat ends\n");
exit(0);
}else{//father
dup2(STDOUT_FILENO,readfd);
close(writefd);
execlp("sort","sort",NULL);
printf("father sort ends\n");
}
int status;
wait(&status);
printf("father exists\n");
return 0;
}
g++ to compile and run this file, after "cat" tihis file, I don't even see "child cat ends", it just hangs.
Where's the problem, how to fix it?
Thanks
1) The order of arguments in dup2 is incorrect. Look at dup2
2) parameters (stdin/stdout) to dup2 are incorrect.
3) The exec() family of functions replace the process image with a new one. So the code after that call does not get to run (unless the exec() fails), so I removed those.
Here is the code:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main(){
int pipefd[2];
pipe(pipefd);
int& readfd = pipefd[0];
int& writefd = pipefd[1];
pid_t pid = fork();
if(pid == 0){ //child
dup2(writefd, 1); // 1 is STDOUT_FILENO -- cat already has input -- needs output
close(readfd);
execlp("cat","cat","tmp.txt", NULL);
perror("execlp() failed in child");
}else{ //father
dup2(readfd, 0); // 0 is STDIN_FILENO -- because sort needs input!
close(writefd);
execlp("sort","sort", NULL);
perror("execlp() failed in parent");
}
return 0;
}
I tried to redirect (write) a Unix command output to a shared memory segment in the child,
and then have the parent read the output back out from the same shared memory segment in the parent process. I don't have a lot of success after few futile attempts. Can anyone show me a way?
thanks in advance.
My code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/shm.h>
#define SHM_SIZE 1024
int main()
{
key_t key; int shmid; char* data;
pid_t cpid=fork();
if (cpid<0)
{
fprintf(stderr,"Fork error!\n");
exit (-1);
}
else if (cpid==0) // child process
{
if ((key = ftok("mysh.c", 'R')) == -1)
{
perror("ftok");
exit(1);
}
// Connect to shared memory
if ((shmid = shmget(key, SHM_SIZE, 0644 | IPC_CREAT)) == -1)
{
perror("shmget");
exit(1);
}
// Attach to the segment
data = shmat(shmid, (void *) 0, 0);
if (data == (char *) (-1))
{
perror("shmat");
exit(1);
}
system("ls -l");
// Stuck: How to redirect the output of "ls -l"
// to a shared memmory segment "data", so that parent process
// can retrieve it later?? Tried to
// do pipe and dup2 but none worked.
// Attempt via read?, but only garbage
read(STDIN_FILENO, data, SHM_SIZE);
}
else
{ // parent process
int st;
wait(&st);
printf("Output read from the child:\n");
if ((write(STDOUT_FILENO, data, SHM_SIZE)) < 0 )
{
perror("write 2");
exit(1);
}
}
}
======================
system("ls -l");
// Stuck: How to redirect the output of "ls -l"
// to a shared memmory segment "data", so that parent process
// can retrieve it later?? Tried to
// do pipe and dup2 but none worked.
For test purpose, I suggest you read from stdin, then write them to data.
Here is an example using POSIX shared memory (POSIX IPC API is better than SYSV IPC API), which child read from stdin to a shared memory region, and parent write the content of this shared memory region to stdout:
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{
const char *shm_name = "/dummy_cat_shm";
int shm_fd;
off_t shm_length;
const char *read_sem_name = "/dummy_cat_read";
const char *write_sem_name = "/dummy_cat_write";
sem_t *read_sem, *write_sem;
pid_t pid;
int buf_length;
char *write_ptr, *read_ptr;
buf_length = 1024;
shm_length = sizeof(buf_length) + buf_length;
/* Create semaphore */
read_sem = sem_open(read_sem_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR, 0);
if (read_sem == SEM_FAILED) {
perror("sem_open");
goto clean_up3;
}
write_sem = sem_open(write_sem_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR, 1);
if (write_sem == SEM_FAILED) {
perror("sem_open");
goto clean_up2;
}
/* Create shared memory segment */
shm_fd = shm_open(shm_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (shm_fd < 0) {
perror("shm_open");
goto clean_up1;
}
if (ftruncate(shm_fd, shm_length) < 0) {
perror("ftruncate");
goto clean_up0;
}
if ((pid = fork()) < 0) {
perror("fork");
goto clean_up0;
}
else if (pid == 0) {
write_ptr = mmap(NULL, shm_length, PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (write_ptr == MAP_FAILED) {
perror("mmap");
goto clean_up0;
}
char *buf = write_ptr+sizeof(buf_length);
while (sem_wait(write_sem) == 0) {
if (fgets(buf, buf_length, stdin) != NULL) {
*(int *)write_ptr = 1;
sem_post(read_sem);
}
else {
*(int *)write_ptr = 0;
sem_post(read_sem);
break;
}
}
munmap(write_ptr, shm_length);
}
else {
read_ptr = mmap(NULL, shm_length, PROT_READ, MAP_SHARED, shm_fd, 0);
if (read_ptr == MAP_FAILED) {
perror("mmap");
goto clean_up0;
}
char *buf = read_ptr + sizeof(buf_length);
while (sem_wait(read_sem) == 0) {
if (*(int *)read_ptr > 0) {
printf("%s", buf);
sem_post(write_sem);
}
else {
break;
}
}
munmap(read_ptr, shm_length);
}
clean_up0:
shm_unlink(shm_name);
clean_up1:
sem_unlink(write_sem_name);
clean_up2:
sem_unlink(read_sem_name);
clean_up3:
exit(EXIT_FAILURE);
}
Note: these two mmap() could be put before fork() in this case.
Compiling:
gcc shm_exp.c -pthread -lrt
Running:
$ ls / | ./a.out
bin/ home/ lib32/ mnt/ run/ sys/ vmlinuz#
boot/ initrd.img# lib64/ opt/ sbin/ tmp/ vmlinuz.old#
dev/ initrd.img.old# lost+found/ proc/ selinux/ usr#
etc/ lib/ media/ root/ srv/ var/
How to redirect stdout of the ls -l
We must shed more light on the processes (parent and children) involved into this code.
How many processes your program creates during its run?
The correct answer is - three.
Two processes are the parent and the explicitly forked child.
The third one is created by the system("ls -l") call.
This function implicitly forks another process that executes (by calling an exec family function) the "ls -l" sell command. What you need to redirect is the output of the child process created by the system() function. It is sad, but the system() does not establish IPC between the participators. If you need to manipulate with the output, do not use system().
I agree with #leeduhem, popen() could be the best approach.
It works exactly as the system(), i.e. forks a new process and executes "ls -l".
In addition, it also establishes a pipe IPC between the participators, so it is easy to catch the child output and to do with it whatever you want:
char buff[1024];
FILE *fd;
// instead of system("ls -l")
fd = popen("ls -l", "r");
// check for errors
while(fgets(buff, sizeof(buff), fd) != NULL)
{
// write to the shared memory
}
pclose(fd);
If you do not want to use the popen() function, you may write a similar one.
The general approach is
open a pipe()
fork() a new process
redirect stdout using dup2
call a suitable exec() function (probably execl()) executing "ls -l"
read from the descriptor you are duplicating by dup2.
how do I intercept calls made from other process which I have called from my process. (say - I call make and I would like to intercept and modify call to gcc from make).
Here is a small example with ptrace:
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sys/user.h>
#include <sys/prctl.h>
const char *sys_call_name(long num);
int main()
{
pid_t pid = fork();
struct user_regs_struct regs;
if (!pid) {
/* child */
while (1) { printf("C\n"); sleep(1); }
}
else { /* parent */
int status = 0;
ptrace(PTRACE_ATTACH, pid, NULL, 0);
ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_SYSCALL) ;
while (1) {
printf("waiting\n");
pid = wait(&status);
/* child gone */
//if (WIFEXITED(status)) { break; }
ptrace(PTRACE_GETREGS, pid, 0, ®s);
/* regs.orig_eax is the system call number */
printf("A system call: %d : %s\n", regs.orig_eax, sys_call_name(regs.orig_eax));
/* let child continue */
ptrace(PTRACE_SYSCALL, pid, NULL, 0);
}
}
return 0;
}
const char *sys_call_name(long num) {
switch(num) {
case 4: return "write";
case 162: return "nanosleep";
case 165: return "getresuid";
case 174: return "rt_sigaction";
case 175: return "rt_sigprocmask";
default: return "unknown";
}
}
It sound from your question that you are looking for Makefile help, specifically you are looking for doing something for all call to the c-compiler.
make allows for any command to be redefined locally -- all you have to do is redefine the macro in make -- for gcc you would simply redefine the CC macros.
You could do that from the command like, like
make CC=echo
which would substitute all call from gcc to echo (not very useful, but you get the idea).
Or you can do it in the Makefile by adding a line like
CC=echo
testprogram: testprogram.o
and when you do make testprogram the make will echo something rather than invoking gcc
You don't easily. The facility in question is the ptrace function, not for the faint of heart.
I have a bunch of mini-server processes running. They're in the same process group as a FastCGI server I need to stop. The FastCGI server will kill everything in its process group, but I need those mini-servers to keep running.
Can I change the process group of a running, non-child process (they're children of PID 1)? setpgid() fails with "No such process" though I'm positive its there.
This is on Fedora Core 10.
NOTE the processes are already running. New servers do setsid(). These are some servers spawned by older code which did not.
One thing you could try is to do setsid() in the miniservers. That will make them session and process group leaders.
Also, keep in mind that you can't change the process group id to one from another session, and that you have to do the call to change the process group either from within the process that you want to change the group of, or from the parent of the process.
I've recently written some test code to periodically change the process group of a set of processes for a very similar task. You need not change the group id periodically, it's just that I thought I might evade a certain script that periodically checked for a group that runs for longer than a certain amount of time. It may also help you track down the error that you get with setpgid():
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
void err(const char *msg);
void prn(const char *msg);
void mydaemon();
int main(int arc, char *argv[]) {
mydaemon();
if (setsid() < 0)
err("setsid");
int secs = 5*60;
/* creating a pipe for the group leader to send changed
group ids to the child */
int pidx[2];
if (pipe(pidx))
err("pipe");
fcntl(pidx[0], F_SETFL, O_NONBLOCK);
fcntl(pidx[1], F_SETFL, O_NONBLOCK);
prn("begin");
/* here the child forks, it's a stand in for the set of
processes that need to have their group ids changed */
int child = fork();
switch (child) {
case -1: err("fork3");
case 0:
close(pidx[1]);
while(1) {
sleep(7);
secs -= 7;
if (secs <= 0) { prn("end child"); exit(0); }
int pid;
/* read new pid if available */
if (read(pidx[0], &pid, sizeof pid) != sizeof pid) continue;
/* set new process group id */
if (setpgid(getpid(), pid)) err("setpgid2");
prn("child group changed");
}
default: break;
}
close(pidx[0]);
/* here the group leader is forked every 20 seconds so that
a new process group can be sent to the child via the pipe */
while (1) {
sleep(20);
secs -= 20;
int pid = fork();
switch (pid) {
case -1: err("fork2");
case 0:
pid = getpid();
/* set process group leader for this process */
if (setpgid(pid, pid)) err("setpgid1");
/* inform child of change */
if (write(pidx[1], &pid, sizeof pid) != sizeof pid) err("write");
prn("group leader changed");
break;
default:
close(pidx[1]);
_exit(0);
}
if (secs <= 0) { prn("end leader"); exit(0); }
}
}
void prn(const char *msg) {
char buf[256];
strcpy(buf, msg);
strcat(buf, "\n");
write(2, buf, strlen(buf));
}
void err(const char *msg) {
char buf[256];
strcpy(buf, msg);
strcat(buf, ": ");
strcat(buf, strerror(errno));
prn(buf);
exit(1);
}
void mydaemon() {
int pid = fork();
switch (pid) {
case -1: err("fork");
case 0: break;
default: _exit(0);
}
close(0);
close(1);
/* close(2); let's keep stderr */
}
After some research I figured it out. Inshalla got the essential problem, "you can't change the process group id to one from another session" which explains why my setpgid() was failing (with a misleading message). However, it seems you can change it from any other process in the group (not necessarily the parent).
Since these processes were started by a FastCGI server and that FastCGI server was still running and in the same process group. Thus the problem, can't restart the FastCGI server without killing the servers it spawned. I wrote a new CGI program which did a setpgid() on the running servers, executed it through a web request and problem solved!
It sounds like you actually want to daemonise the process rather than move process groups. (Note: one can move process groups, but I believe you need to be in the same session and the target needs to already be a process group.)
But first, see if daemonising works for you:
#include <unistd.h>
#include <stdio.h>
int main() {
if (fork() == 0) {
setsid();
if (fork() == 0) {
printf("I'm still running! pid:%d", getpid());
sleep(10);
}
_exit(0);
}
return 0;
}
Obviously you should actually check for errors and such in real code, but the above should work.
The inner process will continue running even when the main process exits. Looking at the status of the inner process from /proc we find that it is, indeed, a child of init:
Name: a.out
State: S (sleeping)
Tgid: 21513
Pid: 21513
PPid: 1
TracerPid: 0