<commit_before>/*
 * ion/mod_statusbar/statusd-launch.c
 *
 * Copyright (c) <NAME> 1999-2009. 
 *
 * See the included file LICENSE for details.
 */
 
#include <sys/time.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>

#include <libtu/minmax.h>
#include <libextl/readconfig.h>
#include <libmainloop/exec.h>
#include <libmainloop/select.h>
#include <libmainloop/signal.h>
#include <ioncore/saveload.h>
#include <ioncore/bindmaps.h>
#include <ioncore/global.h>
#include <ioncore/ioncore.h>

#include "statusbar.h"


#define CF_STATUSD_TIMEOUT_SEC 3

#define BL 1024

#define USEC 1000000

static bool process_pipe(int fd, ExtlFn fn, 
                         bool *doneseen, bool *eagain)
{
    char buf[BL];
    int n;
    bool fnret;
    
    *eagain=FALSE;
    
    n=read(fd, buf, BL-1);
    
    if(n<0){
        if(errno==EAGAIN || errno==EINTR){
            *eagain=(errno==EAGAIN);
            return TRUE;
        }
        warn_err_obj(TR("reading a pipe"));
        return FALSE;
    }else if(n>0){
        buf[n]='\0';
        *doneseen=FALSE;
        return extl_call(fn, "s", "b", &buf, doneseen);
    }
    
    return FALSE;
}


static bool wait_statusd_init(int outfd, int errfd, ExtlFn dh, ExtlFn eh)
{
    fd_set rfds;
    struct timeval tv, endtime, now;
    int nfds=maxof(outfd, errfd);
    int retval;
    bool dummy, doneseen, eagain=FALSE;
    
    if(mainloop_gettime(&endtime)!=0){
        warn_err();
        return FALSE;
    }
    
    now=endtime;
    endtime.tv_sec+=CF_STATUSD_TIMEOUT_SEC;
    
    while(1){
        FD_ZERO(&rfds);

        /* Calculate remaining time */
        if(now.tv_sec>endtime.tv_sec){
            goto timeout;
        }else if(now.tv_sec==endtime.tv_sec){
            if(now.tv_usec>=endtime.tv_usec)
                goto timeout;
            tv.tv_sec=0;
            tv.tv_usec=endtime.tv_usec-now.tv_usec;
        }else{
            tv.tv_usec=USEC+endtime.tv_usec-now.tv_usec;
            tv.tv_sec=-1+endtime.tv_sec-now.tv_sec;
            /* Kernel lameness tuner: */
            tv.tv_sec+=tv.tv_usec/USEC;
            tv.tv_usec%=USEC;
        }
        
        FD_SET(outfd, &rfds);
        FD_SET(errfd, &rfds);
    
        retval=select(nfds+1, &rfds, NULL, NULL, &tv);
        if(retval>0){
            if(FD_ISSET(errfd, &rfds)){
                if(!process_pipe(errfd, eh, &dummy, &eagain))
                    return FALSE;
            }
            if(FD_ISSET(outfd, &rfds)){
                if(!process_pipe(outfd, dh, &doneseen, &eagain))
                    return FALSE;
                if(doneseen){
                    /* Read rest of errors. */
                    bool ok;
                    do{
                        ok=process_pipe(errfd, eh, &dummy, &eagain);
                    }while(ok && !eagain);
                    return TRUE;
                }
            }
        }else if(retval==0){
            goto timeout;
        }
        
        if(mainloop_gettime(&now)!=0){
            warn_err();
            return FALSE;
        }
    }
    
    return TRUE;
    
timeout:
    /* Just complain to stderr, not startup error log, and do not fail.
     * The system might just be a bit slow. We can continue, but without
     * initial values for the meters, geometry adjustments may be necessary
     * when we finally get that information.
     */
    ioncore_warn_nolog(TR("ion-statusd timed out."));
    return TRUE;
}


EXTL_EXPORT
int mod_statusbar__launch_statusd(const char *cmd,
                                  ExtlFn initdatahandler,
                                  ExtlFn initerrhandler,
                                  ExtlFn datahandler,
                                  ExtlFn errhandler)
{
    pid_t pid;
    int outfd=-1, errfd=-1;
    
    if(cmd==NULL)
        return -1;
    
    pid=mainloop_do_spawn(cmd, NULL, NULL,
                          NULL, &outfd, &errfd);
    
    if(pid<0)
        return -1;
    
    if(!wait_statusd_init(outfd, errfd, initdatahandler, initerrhandler))
        goto err;
    
    if(!mainloop_register_input_fd_extlfn(outfd, datahandler))
        goto err;
    
    if(!mainloop_register_input_fd_extlfn(errfd, errhandler))
        goto err2;

    return pid;
    
err2:    
    mainloop_unregister_input_fd(outfd);
err:    
    close(outfd);
    close(errfd);
    return -1;
}

EXTL_EXPORT
int mod_statusbar__terminate_statusd(int pid)
{
    if(pid==0) {
        return -1;
    }

    // Send SIGHUP to the specified statusd process to indicate
    // that we're done.
    kill( (pid_t)pid, SIGHUP);
}
<commit_msg>* Documented reason / bug at head of _terminate_statusd(). * Added a return 0 to the end of _terminate_statusd().<commit_after>/*
 * ion/mod_statusbar/statusd-launch.c
 *
 * Copyright (c) <NAME> 1999-2009. 
 *
 * See the included file LICENSE for details.
 */
 
#include <sys/time.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>

#include <libtu/minmax.h>
#include <libextl/readconfig.h>
#include <libmainloop/exec.h>
#include <libmainloop/select.h>
#include <libmainloop/signal.h>
#include <ioncore/saveload.h>
#include <ioncore/bindmaps.h>
#include <ioncore/global.h>
#include <ioncore/ioncore.h>

#include "statusbar.h"


#define CF_STATUSD_TIMEOUT_SEC 3

#define BL 1024

#define USEC 1000000

static bool process_pipe(int fd, ExtlFn fn, 
                         bool *doneseen, bool *eagain)
{
    char buf[BL];
    int n;
    bool fnret;
    
    *eagain=FALSE;
    
    n=read(fd, buf, BL-1);
    
    if(n<0){
        if(errno==EAGAIN || errno==EINTR){
            *eagain=(errno==EAGAIN);
            return TRUE;
        }
        warn_err_obj(TR("reading a pipe"));
        return FALSE;
    }else if(n>0){
        buf[n]='\0';
        *doneseen=FALSE;
        return extl_call(fn, "s", "b", &buf, doneseen);
    }
    
    return FALSE;
}


static bool wait_statusd_init(int outfd, int errfd, ExtlFn dh, ExtlFn eh)
{
    fd_set rfds;
    struct timeval tv, endtime, now;
    int nfds=maxof(outfd, errfd);
    int retval;
    bool dummy, doneseen, eagain=FALSE;
    
    if(mainloop_gettime(&endtime)!=0){
        warn_err();
        return FALSE;
    }
    
    now=endtime;
    endtime.tv_sec+=CF_STATUSD_TIMEOUT_SEC;
    
    while(1){
        FD_ZERO(&rfds);

        /* Calculate remaining time */
        if(now.tv_sec>endtime.tv_sec){
            goto timeout;
        }else if(now.tv_sec==endtime.tv_sec){
            if(now.tv_usec>=endtime.tv_usec)
                goto timeout;
            tv.tv_sec=0;
            tv.tv_usec=endtime.tv_usec-now.tv_usec;
        }else{
            tv.tv_usec=USEC+endtime.tv_usec-now.tv_usec;
            tv.tv_sec=-1+endtime.tv_sec-now.tv_sec;
            /* Kernel lameness tuner: */
            tv.tv_sec+=tv.tv_usec/USEC;
            tv.tv_usec%=USEC;
        }
        
        FD_SET(outfd, &rfds);
        FD_SET(errfd, &rfds);
    
        retval=select(nfds+1, &rfds, NULL, NULL, &tv);
        if(retval>0){
            if(FD_ISSET(errfd, &rfds)){
                if(!process_pipe(errfd, eh, &dummy, &eagain))
                    return FALSE;
            }
            if(FD_ISSET(outfd, &rfds)){
                if(!process_pipe(outfd, dh, &doneseen, &eagain))
                    return FALSE;
                if(doneseen){
                    /* Read rest of errors. */
                    bool ok;
                    do{
                        ok=process_pipe(errfd, eh, &dummy, &eagain);
                    }while(ok && !eagain);
                    return TRUE;
                }
            }
        }else if(retval==0){
            goto timeout;
        }
        
        if(mainloop_gettime(&now)!=0){
            warn_err();
            return FALSE;
        }
    }
    
    return TRUE;
    
timeout:
    /* Just complain to stderr, not startup error log, and do not fail.
     * The system might just be a bit slow. We can continue, but without
     * initial values for the meters, geometry adjustments may be necessary
     * when we finally get that information.
     */
    ioncore_warn_nolog(TR("ion-statusd timed out."));
    return TRUE;
}


EXTL_EXPORT
int mod_statusbar__launch_statusd(const char *cmd,
                                  ExtlFn initdatahandler,
                                  ExtlFn initerrhandler,
                                  ExtlFn datahandler,
                                  ExtlFn errhandler)
{
    pid_t pid;
    int outfd=-1, errfd=-1;
    
    if(cmd==NULL)
        return -1;
    
    pid=mainloop_do_spawn(cmd, NULL, NULL,
                          NULL, &outfd, &errfd);
    
    if(pid<0)
        return -1;
    
    if(!wait_statusd_init(outfd, errfd, initdatahandler, initerrhandler))
        goto err;
    
    if(!mainloop_register_input_fd_extlfn(outfd, datahandler))
        goto err;
    
    if(!mainloop_register_input_fd_extlfn(errfd, errhandler))
        goto err2;

    return pid;
    
err2:    
    mainloop_unregister_input_fd(outfd);
err:    
    close(outfd);
    close(errfd);
    return -1;
}

/* It looks like child processes generated via fork() on Linux do not
 * get a signal when the parent process dies. Specifically, the
 * PR_SET_PDEATHSIG is cleared, therefore the ion-statusd process must
 * be explicitly terminated on deinit. This function should be called
 * from Lua on deinit with the ion-statusd process id generated above.
 * 
 * For more information, refer to the following man pages:
 * - fork(2)
 * - prctl(2), specifically section on PR_SET_PDEATHSIG
 */
EXTL_EXPORT
int mod_statusbar__terminate_statusd(int pid)
{
    if(pid==0) {
        return -1;
    }

    // Send SIGHUP to the specified statusd process to indicate
    // that we're done.
    kill( (pid_t)pid, SIGHUP);

    return 0;
}

{'fraction_non_alphanumeric': 0.1131309028130395, 'fraction_numerical': 0.008936731999593784, 'mean_word_length': 1.8611272515979083, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1338680', 'n_tokens_mistral': 3446, 'n_tokens_neox': 3316, 'n_words': 736}