////////////////////////////////////////////////////////
// Source: https://russianblogs.com/article/74521821640/
////////////////////////////////////////////////////////
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <linux/wait.h>

int usage(int ver){
    printf("borndefunct 1.1\n"); if(ver) return EXIT_SUCCESS;
    printf("Copyright (c) 2023-2026 Alex Kuryakin daqgroup@mail.ru\n");
    printf("borndefunct - parent process which born a child defunct (zombie) process.\n");
    printf("Parent process forks, wait nc (5) seconds and countdown np (300) seconds.\n");
    printf("Child process wait nc (5) seconds, die and becomes defunct Zombie.\n");
    printf("This tool uses for testing purposes.\n");
    printf("usage:\n");
    printf(" borndefunct [[np] [nc]] | [-c 'command' [np]]\n");
    printf("  np - number of parent loops, default 300\n");
    printf("  nc - number of child  loops, default 5\n");
    printf("example:\n");
    printf(" borndefunct 300 5\n");
    printf(" borndefunct -c 'exec zenity --info --text \"Born zombie process...\"' 300\n");
    return EXIT_SUCCESS;
}

static int np=300;
static int nc=5;

static int terminated=0;

void sighandler(int sig)
{
    if (sig == SIGINT) terminated++;
}

int command(int argc,char **argv){
    if(argc>3){ if(atoi(argv[3])>0) np=atoi(argv[3]); }
    if(argc<3){
        printf("Error: invalid call format.\n");
        printf("Usage: %s -c 'command'\n",argv[0]);
        fflush(stdout);
        return EXIT_FAILURE;
    }
    pid_t pid=fork();
    if(pid==-1) {
        printf("could not fork\n");
        fflush(stdout);
        return EXIT_FAILURE;
    }
    else if(pid==0) {
        printf("child  pid is %d\n",getpid());
        printf("exec: %s %s '%s'\n","/bin/bash",argv[1],argv[2]);
        fflush(stdout);
        char *args[]={"/bin/bash",argv[1],argv[2],NULL};
        execv("/bin/bash",args);
        exit(127);
    }else if(pid>0) {
        printf("parent pid is %d\n",getpid());
        printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
        printf("!!! Press ^C to BREAK countdown !!!\n");
        printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
        fflush(stdout);
        signal(SIGINT,sighandler);
        sleep(1);
        int i=np;
        while(i>0) {
            if(terminated) break;
            printf("\rparent countdown %-10d",i);
            fflush(stdout);
            sleep(1);
            i--;
        }
        int status=0; pid_t wpid=waitpid(pid,&status,WNOHANG);
        if (wpid == -1) { perror("\nwaitpid failed"); exit(EXIT_FAILURE); }
        if (WIFEXITED(status)) { printf("\rchild exited (status %d)\n", WEXITSTATUS(status)); }
        if (WIFSIGNALED(status)) { printf("\rchild killed (signal %d)\n", WTERMSIG(status)); }
        if (WIFSTOPPED(status)) { printf("\rchild stopped (signal %d)\n", WSTOPSIG(status)); }
        printf("\rparent is over and clean his child defunct Zombie.\n");
        fflush(stdout);
    }
    return EXIT_SUCCESS;
}

int main(int argc,char **argv){
    if(argc>1){ if(strcmp(argv[1],"-h")==0) return usage(0); }
    if(argc>1){ if(strcmp(argv[1],"--help")==0) return usage(0); }
    if(argc>1){ if(strcmp(argv[1],"--version")==0) return usage(1); }
    if(argc>1){ if(strcmp(argv[1],"-c")==0) return command(argc,argv); }
    if(argc>1){ if(atoi(argv[1])>0) np=atoi(argv[1]); }
    if(argc>2){ if(atoi(argv[2])>0) nc=atoi(argv[2]); }
    pid_t pid=fork();
    if(pid==-1) {
        printf("could not fork\n");
        fflush(stdout);
        return EXIT_FAILURE;
    }
    else if(pid==0) {
        printf("child  pid is %d\n",getpid());
        fflush(stdout);
        signal(SIGINT,sighandler);
        int i=nc;
        while(i>0) {
            if(terminated) break;
            printf("\rchild  countdown %-10d",i);
            fflush(stdout);
            sleep(1);
            i--;
        }
        printf("\rchild  is over and became defunct Zombie.\n");
        fflush(stdout);
    }else if(pid>0) {
        printf("parent pid is %d\n",getpid());
        printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
        printf("!!! Press ^C to BREAK countdown !!!\n");
        printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
        fflush(stdout);
        signal(SIGINT,sighandler);
        sleep(nc+1);
        int i=np;
        while(i>0) {
            if(terminated) break;
            printf("\rparent countdown %-10d",i);
            fflush(stdout);
            sleep(1);
            i--;
        }
        int status=0; pid_t wpid=waitpid(pid,&status,WNOHANG);
        if (wpid == -1) { perror("\nwaitpid failed"); exit(EXIT_FAILURE); }
        if (WIFEXITED(status)) { printf("\rchild exited (status %d)\n", WEXITSTATUS(status)); }
        if (WIFSIGNALED(status)) { printf("\rchild killed (signal %d)\n", WTERMSIG(status)); }
        if (WIFSTOPPED(status)) { printf("\rchild stopped (signal %d)\n", WSTOPSIG(status)); }
        printf("\rparent is over and clean his child defunct Zombie.\n");
        fflush(stdout);
    }
    return EXIT_SUCCESS;
}
