I'm trying to write a C program that restarts itself once. My approach was to fork and in the child process call execve. I thought I would see Hello twice printed. This here prints main twice, but even so if I comment out execve, so I assume I'm not correctly using execve here. The binary is called "restartOnce".
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
int main(int argc, char *argv[]) {
pid_t pid = fork();
printf("Main.\n");
if (pid == 0) {
char *argv = "hello"; // To silence gcc compiler warning.
char *env = "world"; // To silence gcc compiler warning.
execve("restartOnce", &argv, &env);
} else {
wait(NULL);
printf("Done.\n");
}
return 0;
}
I'm not completely sure what you're trying to accomplish, but I see two things to attend to. We have to get the execve() call right, plus we have to avoid a fork bomb, and this code should do it.
There are lots of ways to avoid a fork bomb, but I chose to set something in the environment, and when the child sees this environment variable, it will know not to continue.
So execve() requires its own argv, which is an array of pointers to individual strings (with a NULL pointer at the end), and it's entirely legit to pass the same parameter you got from main.
The third parameter is like argv, but it's a list of environment variables instead of command line arguments. It has things like TERM=vt100, PATH=/bin/..., and many others. Though you can fetch environment variables individually with getenv("PATH"), Unix/Linux systems provide an argv-like variable environ.
This variable is declared in <unistd.h> but requires the _GNU_SOURCE macro to be defined to expose it.
This done, you can call safely call execve this way:
#define _GNU_SOURCE
#include <unistd.h>
...
execve("restartOnce", argv, environ);
To avoid a fork bomb, one of the first thing the program does is look for the environment variable FORKBOMB - if this is set, we're in the child process and should stop forking other children, perhaps doing something else instead (the real work of the program?)
But if we're in the parent - no variable seen - we actively set the variable by putting it in our own environment that's seen by the child processes: putenv("FORKBOMB=no");
A minor addition: the very first thing the program does is report that it's started up, and it provides its own process ID just so you know what it's doing.
Maybe I misunderstand the problem, but this is how I'd solve what I imagine you're asking:
#define _GNU_SOURCE // for "environ"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
int main(int argc, char *argv[])
{
printf("Main in pid %d.\n", getpid() );
if (getenv("FORKBOMB") != 0)
{
printf("**We're in the second child, no more forking\n");
exit(0);
}
putenv("FORKBOMB=no");
pid_t pid = fork();
if (pid == 0) {
execve("restartOnce", argv, environ);
} else {
wait(NULL);
printf("Done.\n");
}
return 0;
}
There are quite a few other ways to avoid the fork bomb, but this is as good as any to get the idea across, and others might chime in with their favorites.
EDIT But as I think about this, I'm pretty sure this should not be necessary no matter what you're trying to accomplish unless this is just a learning exercise.
The two things I can imagine are:
1) You need to detach from the parent process so you can run in the background, like a daemon process. In that case, you don't have to exec, just do the daemon work in the child process.
2) If the program has altered itself - perhaps downloaded an updated version of itself, then it does have to call exec to get the new executable, but a fork() would not be required.
So I'm not sure we (or at least I) know what you're trying to accomplish.
Related
Within the child process, is there any way that it determine whether it was launched as a fork with overlay memory, or a vfork with shared memory?
Basically, our logging engine needs to be much more careful (and not log some classes of activity) in vfork. In fork it needs to cooperate with the parent process in ways that it doesn't in vfork. We know how to do those two things, but not how to decide.
I know I could probably intercept the fork/vfork/clone calls, and store the fork/vfork/mapping status as a flag, but it would make life somewhat simpler if there was an API call the child could make to determine its own state.
Extra marks: Ideally I also need to pick up any places in libraries that have done a fork or vfork and then called back into our code. And how that can happen? At least one of the libraries we have offers a popen-like API where a client call-back is called from the fork child before the exec. Clearly the utility of that call-back is significantly restricted in vfork.
All code not specifically designed to work under vfork() doesn't work under vfork().
Technically, you can check if you're in a vfork() child by calling mmap() and checking if the memory mapping was inherited by the parent process under /proc. Do not write this code. It's a really bad idea and nobody should be using it. Really, the best way to tell if you're in a vfork() child or not is to be passed that information. But here comes the punchline. What are you going to do with it?
The things you can't do as a vfork() child include calling fprintf(), puts(), fopen(), or any other standard I/O function, nor malloc() for that matter. Unless the code is very carefully designed, you're best off not calling into your logging framework at all, and if it is carefully designed you don't need to know. A better design would most likely be log your intent before calling vfork() in the first place.
You ask in comments about a library calling fork() and then back into your code. That's already kind of bad. But no library should ever ever call vfork() and back into your code without being explicitly documented as doing so. vfork() is a constrained environment and calling things not expected to be in that environment really should not happen.
A simple solution could use pthread_atfork(). The callbacks registered with this service are triggered only upon fork(). So, the 3rd parameter of the function, which is called in the child process right after the fork, could update a global variable. The child can check the variable and if it is modified, then it has been forked:
/*
Simple program which demonstrates a solution to
make the child process know if it has been forked or vforked
*/
#include <pthread.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
pid_t forked;
void child_hdl(void)
{
forked = getpid();
}
int main(void)
{
pid_t pid;
pthread_atfork(0, 0, child_hdl);
pid = fork();
if (pid == 0) {
if (forked != 0) {
printf("1. It is a fork()\n");
}
exit(0);
}
// Father continues here
wait(NULL);
pid = vfork();
if (pid == 0) {
if (forked != 0) {
printf("2. It is a fork()\n");
}
_exit(0);
}
// Father continues here
wait(NULL);
return 0;
}
Build/execution:
$ gcc fork_or_vfork.c
$ ./a.out
1. It is a fork()
I came across kcmp today, which looks like it can answer the basic question - i.e. do two tids or pids share the same VM. If you know they represent forked parent/child pids, this can perhaps tell you if they are vfork()ed.
Of course if they are tids in the same process group then they will by definition share VM.
https://man7.org/linux/man-pages/man2/kcmp.2.html
int syscall(SYS_kcmp, pid_t pid1, pid_t pid2, int type,
unsigned long idx1, unsigned long idx2);
KCMP_VM
Check whether the processes share the same address space.
The arguments idx1 and idx2 are ignored. See the
discussion of the CLONE_VM flag in clone(2).
If you were created by vfork, your parent will be waiting for you to terminate. Otherwise, it's still running. Here's some very ugly code:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
void doCheck()
{
char buf[512];
sprintf(buf, "/proc/%d/wchan", (int) getppid());
int j = open(buf, O_RDONLY);
if (j < 0) printf("No open!\n");
int k = read(j, buf, 500);
if (k <= 0) printf("k=%d\n", k);
close(j);
buf[k] = 0;
char *ptr = strstr(buf, "vfork");
if (ptr != NULL)
printf("I am the vfork child!\n");
else
printf("I am the fork child!\n");
}
int main()
{
if (fork() == 0)
{
doCheck();
_exit(0);
}
sleep(1);
if (vfork() == 0)
{
doCheck();
_exit(0);
}
sleep(1);
}
This is not perfect, the parent might be waiting for a subsequent vfork call to complete.
I'm not familiar with C at all.
How do I start a child process? This child process is going to execute the specified command with a call to execve(). It will try to search among the file directory specified in the environment variable PATH after the command can be
found as executable file.
I've done this so far:
//Do commands
pid_t childId;
// Fork the child process
child_id = safefork.c(); //can't use fork();
safefork.c
Code provided by the tutor; do not damn the messenger!
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/errno.h>
extern int errno;
#define MAX_PROCESSES 6
static int n_processes(void)
{
return system("exit `/bin/ps | /store/bin/wc -l`")/256;
}
pid_t safefork(void)
{
static int n_initial = -1;
if (n_initial == -1) /* Første gang funksjonen kalles: */
n_initial = n_processes();
else if (n_processes() >= n_initial+MAX_PROCESSES) {
sleep(2);
errno = EAGAIN;
return (pid_t)-1;
}
return fork();
}
Fixing your code
In your code, get the name of the variable spelled consistently (child_id is not the same as childId).
pid_t child_id = simplefork();
if (child_id < 0)
{
...handle error...
}
else if (child_id == 0)
{
...do childish code - execve() etc...
}
else
{
...do parental code - waitpid() etc...
}
Note that the fork() call within the simplefork() function that you're given to use is responsible for creating the new process. That's all it takes; that's the way it's done for all processes except the very first process.
Why not fork()?
What do you mean by "can't use fork()"? The main alternative mechanism is vfork(), which is a very restricted variant of fork() (do not use it); or maybe you could use posix_spawn() or posix_spawnp() — which are incredibly complex alternatives. I don't think there are any other options.
After forking, you might be able to use execvp() instead of execve() — it will do the path search for you. Unless, of course, the purpose of the exercise is to implement execvp() in terms of execve().
Your code uses the notation safefork.c(), but that is not usually correct in C; I could devise a structure type that would make it work, but it probably isn't what you meant.
We got another file called safefork.c — we are not allowed to use fork, only safefork which is already given.
[…before the code was posted]
OK; that's very curious. Presumably, you got a header safefork.h which declares whatever function you're supposed to use (perhaps extern pid_t safefork(void);), and the file safefork.c which does something to wrap around fork(). 'Tis odd: I don't think fork() is a dangerous function. I'd be curious to see what the 'safe fork' does, but I'm sceptical that it is significantly safer than the standard fork function. (I suppose it could does some things like fflush(0) before invoking fork(), or do an error exit if the fork() fails, but that's pushing the envelope.)
[…after the code was posted]
A critique of the code for safefork(), which I fully recognize is not your own code but code that is given to you to use.
The code for safefork() is an abomination. It runs a shell via system() which runs ps and wc to find out how many processes you currently have running, and goes to sleep for 2 seconds if you can't do the fork() because there are too many processes running (more than 6, maybe including the 3 that the safefork() is running!) and then returns "I failed". Someone needs their head seeing to (and no, that isn't you; it is the author of the code).
Oh, and extern int errno; is incorrect; the only safe way to declare errno is by #include <errno.h>. Negative marks to the teacher for that blunder. It is not a good idea to #include <sys/errno.h>; #include <sys/types.h> is not often needed in modern POSIX — from POSIX 2008 onwards at any rate; it may have been unnecessary before that). In the context of the safefork.h header, making it self-contained does require #include <sys/types.h>.
Even assuming that safefork() is a good idea (it isn't), it should be implemented as shown below.
safefork.h
#ifndef SAFEFORK_H_INCLUDED
#define SAFEFORK_H_INCLUDED
#include <sys/types.h> // pid_t
extern pid_t safefork(void);
#endif
safefork.c
#include "safefork.h"
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_PROCESSES 6
static int n_processes(void)
{
return system("exit `/bin/ps | /store/bin/wc -l`") / 256;
}
pid_t safefork(void)
{
static int n_initial = -1;
if (n_initial == -1)
n_initial = n_processes();
else if (n_processes() >= n_initial+MAX_PROCESSES)
{
errno = EAGAIN;
return (pid_t)-1;
}
return fork();
}
I am writing a program in C in one of my systems classes. We write c code and run it in a unix environment. I have looked all over the internet, but can't seem to find any way to make the kill(int PID) command work. The code will compile and run fine, but if I use the
ps -u username
command in a unix command prompt (after execution has completed of course,) it says that the all of the processes I tried to kill in my c code are still running. I can kill them from the unix command prompt by manually entering their PIDs, but for the life of me, I cannot figure out how to do it inside of my program.
In this particular program, I am trying to kill process CC, which is a process that just infinitely calls usleep(100); until terminated.
I tried using kill(C3, -9); and variations of execlp("kill", "kill", C3, (char *)0); but still no luck. Does anyone have any idea what I am doing wrong here? My only guess is that the kill command is being passed the wrong PID parameter, but if that's the case, I have no idea how I would get the correct one.
EDIT: Also, the kill command returns a value of zero, which I believe means that it "succeeded" in executing the command.
EDIT: Just noticed that the solution to my problem was in the instructions for the assignment all along. Yup. I'm stupid.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int args, char* argv[])
{
//
//Step 7
//
//Create process C3
int C3=fork();
if (C3==0)
{
execlp("CC", "CC", (char *)0);
}
else
{
usleep(500000);
//
//Step 8
//
int ps=fork();
if (ps==0)
{
execlp("ps", "ps", "-u", "cooley", (char *)0);
}
else
{
wait(NULL);
kill(C3);
}
}
exit(0);
}
You're calling the kill system call with only one argument, when it takes two. This leads to undefined behavior since then the second argument can by anything. You should get a warning about this when compiling.
The second argument should be a value from <signal.h> (see the signal(7) manual page).
After a long time of researching for my problem, I have really no idea how I can solve it.
My question is that I need the C source code for something like this:
ls &
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void) {
int new_pid;
int status;
new_pid = fork();
if (new_pid == 0) {
execlp("ls", "ls", "-l", NULL);
} else {
waitpid(new_pid, &status, 0);
}
return 0;
}
If I code it like this, 'ls -l' will be executed, but it wasnt forked to the background.
It is just an example 'ls -l', it could also be 'xournal &;', 'libreoffice &' or something like this.
My main problem is that I have no idea how to code '&' in C.
Can anyone please provide me a tip or even a solution for this?
When you execute another program, you need to call these functions:
new_pid = fork();
exec(...); //in the child only
If you want to wait until that new process ends (i.e. not specifying the & ) you call
waitpid(new_pid, ...);
That way, your shell blocks until the process you launced is finished.
If you don't want to wait, but just continue operating your shell (i.e. specifying the &) you simply do not call waitpid().
Use system("ls &").
It'd help if you explained why you wanted to run something in the background.
How can I implement chmod command on file by using exec? I would appreciate if anyone can provide me a code.
I'm not going to show you a working model, but execve() works like this:
char *args[] = {"foo", "argument1", "argument2", (char *)NULL};
... handle forking ....
res = execve("/sbin/foo", args, (char *)NULL);
... handle execve() failing ....
The third argument to execve() is left as an exercise for the reader to research, NULL may or may not be suitable for your assignment. Additionally, its up to you to determine what type res should be and what it should equal on success. Notice casting NULL.
The single UNIX specification is usually a good place to start.
From C code, directly calling chmod(2) will almost certainly be a better choice than going through the whole hassle of fork()ing and exec()ing.
Admittedly, most of that hassle is the fork() part, and if your program does not need to do anything else after the exec() call, then just running one of the exec() family functions without forking is reasonably fine (for an exercise in using exec(), that is).
try this: http://support.sas.com/documentation/onlinedoc/sasc/doc/lr2/execve.htm
also see: http://linux.about.com/library/cmd/blcmdl3_execvp.htm
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
main()
{
pid_t pid;
char *parmList[] = {"/bin/chmod", "0700", "/home/op/biaoaai/bead",NULL};
if ((pid = fork()) ==-1) //fork failed
perror("fork error");
else if (pid == 0) { //child (pid==0 if child)
execvp("chmod", parmList);
printf("Return not expected. Must be an execve error.n");
} else { //parent (pid==child process)
//parent specific code goes here
}
}
Edit: I never actually compiled... updated with users working paramList.