Welcome to little lamb

Code » anopa » master » tree

[master] / src / utils / aa-service.c

/*
 * anopa - Copyright (C) 2015-2017 Olivier Brunel
 *
 * aa-service.c
 * Copyright (C) 2015-2018 Olivier Brunel <jjk@jjacky.com>
 *
 * This file is part of anopa.
 *
 * anopa is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * anopa is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * anopa. If not, see http://www.gnu.org/licenses/
 */

#include <getopt.h>
#include <unistd.h>
#include <skalibs/djbunix.h>
#include <skalibs/env.h>
#include <skalibs/bytestr.h>
#include <skalibs/sgetopt.h>
#include <skalibs/genalloc.h>
#include <skalibs/stralloc.h>
#include <execline/execline.h>
#include <anopa/common.h>
#include <anopa/output.h>

#define RC_ST_FAIL      (1 << 1)

typedef struct exlsn_s exlsn_t;
struct exlsn_s
{
  stralloc vars;
  stralloc values;
  genalloc data; /* array of elsubst */
  stralloc modifs;
};
#define EXLSN_ZERO { .vars = STRALLOC_ZERO, .values = STRALLOC_ZERO, .data = GENALLOC_ZERO, .modifs = STRALLOC_ZERO }


enum
{
    ERR_BAD_KEY = 1,
    ERR_ADDVAR,
    ERR_DIRNAME,
    ERR_NOT_LOG
};

static char islog = 0;

static int
addvar (const char *name, const char *value, exlsn_t *info)
{
    eltransforminfo_t si = ELTRANSFORMINFO_ZERO;
    elsubst_t blah;

    blah.var = info->vars.len;
    blah.value = info->values.len;

    if (el_vardupl (name, info->vars.s, info->vars.len))
        return -ERR_BAD_KEY;
    if (!stralloc_catb (&info->vars, name, strlen (name) + 1))
        return -ERR_ADDVAR;
    if (!stralloc_cats (&info->values, value))
        goto err;

    {
        register int r;

        r = el_transform (&info->values, blah.value, &si);
        if (r < 0)
            goto err;
        blah.n = r ;
    }

    if (!genalloc_append (elsubst_t, &info->data, &blah))
        goto err;

    return 0;

err:
    info->vars.len = blah.var;
    info->values.len = blah.value;
    return -ERR_ADDVAR;
}

static int
aa_service (exlsn_t *info)
{
    stralloc sa = STRALLOC_ZERO;
    char *s;
    size_t len;
    size_t n;
    int r;

    if (sagetcwd (&sa) < 0)
        return -ERR_DIRNAME;

    n = byte_rchr (sa.s, sa.len, '/');
    if (n == sa.len)
    {
        r = -ERR_DIRNAME;
        goto err;
    }
    /* current dirname only */
    s = sa.s + n + 1;

    if (islog)
    {
        if (str_diff (s, "log"))
        {
            r = -ERR_NOT_LOG;
            goto err;
        }

        /* i.e. sa.s = "/log" */
        if (n <= 0)
        {
            r = -ERR_NOT_LOG;
            goto err;
        }

        /* use parent's dirname instead, i.e. the service we're logger of */
        sa.s[n] = '\0';
        n = byte_rchr (sa.s, n - 1, '/');
        s = sa.s + n + 1;

        if (s > sa.s + sa.len)
        {
            r = -ERR_DIRNAME;
            goto err;
        }
    }

    r = addvar ("SERVICE_NAME", s, info);
    if (r < 0)
        goto err;

    len = strlen (s);
    n = byte_chr (s, len, '@');
    if (n < len)
        s[n] = '\0';
    r = addvar ("SERVICE", s, info);
    if (r < 0)
        goto err;

    r = addvar ("INSTANCE", (n < len) ? s + n + 1 : "", info);
    if (r < 0)
        goto err;

err:
    stralloc_free (&sa);
    return r;
}

static void
dieusage (int rc)
{
    aa_die_usage (rc, "[OPTION] PROG...",
            " -D, --double-output           Enable double-output mode\n"
            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
            " -l, --log                     Use parent directory as servicedir\n"
            " -h, --help                    Show this help screen and exit\n"
            " -V, --version                 Show version information and exit\n"
            );
}

int
main (int argc, char const **argv, char const *const *envp)
{
    PROG = "aa_service";
    exlsn_t info = EXLSN_ZERO;
    stralloc sa = STRALLOC_ZERO;
    stralloc dst = STRALLOC_ZERO;
    int r;

    for (;;)
    {
        struct option longopts[] = {
            { "double-output",      no_argument,        NULL,   'D' },
            { "help",               no_argument,        NULL,   'h' },
            { "log",                no_argument,        NULL,   'l' },
            { "log-file",           required_argument,  NULL,   'O' },
            { "version",            no_argument,        NULL,   'V' },
            { NULL, 0, 0, 0 }
        };
        int c;

        c = getopt_long (argc, (char * const *) argv, "+DhlO:V", longopts, NULL);
        if (c == -1)
            break;
        switch (c)
        {
            case 'D':
                aa_set_double_output (1);
                break;

            case 'h':
                dieusage (RC_OK);

            case 'l':
                islog = 1;
                break;

            case 'O':
                aa_set_log_file_or_die (optarg);
                break;

            case 'V':
                aa_die_version ();

            default:
                dieusage (RC_FATAL_USAGE);
        }
    }
    argc -= optind;
    argv += optind;

    r = aa_service (&info);
    if (r < 0)
        switch (-r)
        {
            case ERR_NOT_LOG:
                aa_strerr_dief1x (RC_FATAL_USAGE, "option --log used while not in a subfolder 'log'");

            case ERR_DIRNAME:
                aa_strerr_diefu1x (RC_FATAL_IO, "get current dirname");

            case ERR_BAD_KEY:
                aa_strerr_dief1x (RC_ST_FAIL, "bad substitution key");

            case ERR_ADDVAR:
                aa_strerr_diefu1sys (RC_ST_FAIL, "complete addvar function");

            default:
                aa_strerr_diefu2x (RC_ST_FAIL, "complete addvar function", ": unknown error");
        }

    if (!env_string (&sa, argv, (unsigned int) argc))
        aa_strerr_diefu1sys (RC_FATAL_MEMORY, "env_string");

    r = el_substitute (&dst, sa.s, sa.len, info.vars.s, info.values.s,
            genalloc_s (elsubst_t const, &info.data),
            genalloc_len (elsubst_t const, &info.data));
    if (r < 0)
        aa_strerr_diefu1sys (RC_ST_FAIL, "el_substitute");
    else if (r == 0)
        _exit (0);

    stralloc_free (&sa);

    {
        char const *v[r + 1];

        if (!env_make (v, r, dst.s, dst.len))
            aa_strerr_diefu1sys (RC_ST_FAIL, "env_make");
        v[r] = 0;
        pathexec_r (v, envp, env_len (envp), info.modifs.s, info.modifs.len);
    }

    aa_strerr_dieexec (RC_FATAL_EXEC, dst.s);
}