openrc/src/libeinfo.c
Roy Marples 5af58b4514 Rewrite the core parts in C. We now provide librc so other programs can
query runlevels, services and state without using bash. We also provide
libeinfo so other programs can easily use our informational functions.

As such, we have dropped the requirement of using bash as the init script
shell. We now use /bin/sh and have strived to make the scripts as portable
as possible. Shells that work are bash and dash. busybox works provided
you disable s-s-d. If you have WIPE_TMP set to yes in conf.d/bootmisc you
should disable find too.
zsh and ksh do not work at this time.

Networking support is currently being re-vamped also as it was heavily bash
array based. As such, a new config format is available like so
config_eth0="1.2.3.4/24 5.6.7.8/16"
or like so
config_eth0="'1.2.3.4 netmask 255.255.255.0' '5.6.7.8 netmask 255.255.0.0'"

We will still support the old bash array format provided that /bin/sh IS
a link it bash.

ChangeLog for baselayout-1 can be found in our SVN repo.
2007-04-05 11:18:42 +00:00

878 lines
16 KiB
C

/*
einfo.c
Gentoo informational functions
Copyright 2007 Gentoo Foundation
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "einfo.h"
#include "rc.h"
#include "rc-misc.h"
/* Incase we cannot work out how many columns from ioctl, supply a default */
#define DEFAULT_COLS 80
#define OK "ok"
#define NOT_OK "!!"
#define CHECK_VERBOSE if (! is_env ("RC_VERBOSE", "yes")) return 0
/* Number of spaces for an indent */
#define INDENT_WIDTH 2
/* How wide can the indent go? */
#define INDENT_MAX 40
#define EBUFFER_LOCK RC_SVCDIR "ebuffer/.lock"
/* A cheat sheet of colour capable terminals
This is taken from DIR_COLORS from GNU coreutils
We embed it here as we shouldn't depend on coreutils */
static const char *colour_terms[] =
{
"Eterm",
"ansi",
"color-xterm",
"con132x25",
"con132x30",
"con132x43",
"con132x60",
"con80x25",
"con80x28",
"con80x30",
"con80x43",
"con80x50",
"con80x60",
"cons25",
"console",
"cygwin",
"dtterm",
"gnome",
"konsole",
"kterm",
"linux",
"linux-c",
"mach-color",
"mlterm",
"putty",
"rxvt",
"rxvt-cygwin",
"rxvt-cygwin-native",
"rxvt-unicode",
"screen",
"screen-bce",
"screen-w",
"screen.linux",
"vt100",
"xterm",
"xterm-256color",
"xterm-color",
"xterm-debian",
NULL
};
static bool is_env (const char *var, const char *val)
{
char *v;
if (! var)
return (false);
v = getenv (var);
if (! v)
return (val == NULL ? true : false);
return (strcasecmp (v, val) == 0 ? true : false);
}
bool colour_terminal (void)
{
static int in_colour = -1;
int i = 0;
char *term;
if (in_colour == 0)
return (false);
if (in_colour == 1)
return (true);
term = getenv ("TERM");
/* If $TERM isn't set then the chances are we're in single user mode */
if (! term)
return (true);
while (colour_terms[i])
{
if (strcmp (colour_terms[i], term) == 0)
{
in_colour = 1;
return (true);
}
i++;
}
in_colour = 0;
return (false);
}
static int get_term_columns (void)
{
#ifdef TIOCGSIZE /* BSD */
struct ttysize ts;
if (ioctl(0, TIOCGSIZE, &ts) == 0)
return (ts.ts_cols);
#elif TIOCGWINSZ /* Linux */
struct winsize ws;
if (ioctl(0, TIOCGWINSZ, &ws) == 0)
return (ws.ws_col);
#endif
return (DEFAULT_COLS);
}
static int ebuffer (const char *cmd, int retval, const char *fmt, va_list ap)
{
char *file = getenv ("RC_EBUFFER");
FILE *fp;
char buffer[RC_LINEBUFFER];
int l = 1;
if (! file || ! cmd || strlen (cmd) < 4)
return (0);
if (! (fp = fopen (file, "a")))
{
fprintf (stderr, "fopen `%s': %s\n", file, strerror (errno));
return (0);
}
fprintf (fp, "%s %d ", cmd, retval);
if (fmt)
{
l = vsnprintf (buffer, sizeof (buffer), fmt, ap);
fprintf (fp, "%d %s\n", l, buffer);
}
else
fprintf (fp, "0\n");
fclose (fp);
return (l);
}
typedef struct func
{
const char *name;
int (*efunc) (const char *fmt, ...);
int (*eefunc) (int retval, const char *fmt, ...);
void (*eind) (void);
} func_t;
static const func_t funcmap[] = {
{ "einfon", &einfon, NULL, NULL },
{ "ewarnn", &ewarnn, NULL, NULL},
{ "eerrorn", &eerrorn, NULL, NULL},
{ "einfo", &einfo, NULL, NULL },
{ "ewarn", &ewarn, NULL, NULL },
{ "eerror", &eerror, NULL, NULL },
{ "ebegin", &ebegin, NULL, NULL },
{ "eend", NULL, &eend, NULL },
{ "ewend", NULL, &ewend, NULL },
{ "eindent", NULL, NULL, &eindent },
{ "eoutdent", NULL, NULL, &eoutdent },
{ "veinfon", &veinfon, NULL, NULL },
{ "vewarnn", &vewarnn, NULL, NULL },
{ "veinfo", &veinfo, NULL, NULL },
{ "vewarn", &vewarn, NULL, NULL },
{ "vebegin", &vebegin, NULL, NULL },
{ "veend", NULL, &veend, NULL },
{ "vewend", NULL, &vewend, NULL },
{ "veindent" ,NULL, NULL, &veindent },
{ "veoutdent", NULL, NULL, &veoutdent },
{ NULL, NULL, NULL, NULL },
};
void eflush (void)
{
FILE *fp;
char *file = getenv ("RC_EBUFFER");
char buffer[RC_LINEBUFFER];
char *cmd;
int retval = 0;
int length = 0;
char *token;
char *p;
struct stat buf;
pid_t pid;
char newfile[PATH_MAX];
int i = 1;
if (! file|| (stat (file, &buf) != 0))
{
errno = 0;
return;
}
/* Find a unique name for our file */
while (true)
{
snprintf (newfile, sizeof (newfile), "%s.%d", file, i);
if (stat (newfile, &buf) != 0)
{
if (rename (file, newfile))
fprintf (stderr, "rename `%s' `%s': %s\n", file, newfile,
strerror (errno));
break;
}
i++;
}
/* We fork a child process here so we don't hold anything up */
if ((pid = fork ()) == -1)
{
fprintf (stderr, "fork: %s", strerror (errno));
return;
}
if (pid != 0)
return;
/* Spin until we can lock the ebuffer */
while (true)
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 20000;
select (0, NULL, NULL, NULL, &tv);
errno = 0;
if (link (newfile, EBUFFER_LOCK) == 0)
break;
if (errno != EEXIST)
fprintf (stderr, "link `%s' `%s': %s\n", newfile, EBUFFER_LOCK,
strerror (errno));
}
if (! (fp = fopen (newfile, "r")))
{
fprintf (stderr, "fopen `%s': %s\n", newfile, strerror (errno));
return;
}
unsetenv ("RC_EBUFFER");
memset (buffer, 0, RC_LINEBUFFER);
while (fgets (buffer, RC_LINEBUFFER, fp))
{
i = strlen (buffer) - 1;
if (i < 1)
continue;
if (buffer[i] == '\n')
buffer[i] = 0;
p = buffer;
cmd = strsep (&p, " ");
token = strsep (&p, " ");
if (sscanf (token, "%d", &retval) != 1)
{
fprintf (stderr, "eflush `%s': not a number", token);
continue;
}
token = strsep (&p, " ");
if (sscanf (token, "%d", &length) != 1)
{
fprintf (stderr, "eflush `%s': not a number", token);
continue;
}
i = 0;
while (funcmap[i].name)
{
if (strcmp (funcmap[i].name, cmd) == 0)
{
if (funcmap[i].efunc)
{
if (p)
funcmap[i].efunc ("%s", p);
else
funcmap[i].efunc (NULL, NULL);
}
else if (funcmap[i].eefunc)
{
if (p)
funcmap[i].eefunc (retval, "%s", p);
else
funcmap[i].eefunc (retval, NULL, NULL);
}
else if (funcmap[i].eind)
funcmap[i].eind ();
else
fprintf (stderr, "eflush `%s': no function defined\n", cmd);
break;
}
i++;
}
if (! funcmap[i].name)
fprintf (stderr, "eflush `%s': invalid function\n", cmd);
}
fclose (fp);
if (unlink (EBUFFER_LOCK))
fprintf (stderr, "unlink `%s': %s", EBUFFER_LOCK, strerror (errno));
if (unlink (newfile))
fprintf (stderr, "unlink `%s': %s", newfile, strerror (errno));
_exit (EXIT_SUCCESS);
}
#define EBUFFER(_cmd, _retval, _fmt, _ap) \
{ \
int _i = ebuffer (_cmd, _retval, _fmt, _ap); \
if (_i) \
return (_i); \
}
static void elog (int level, const char *fmt, va_list ap)
{
char *e = getenv ("RC_ELOG");
if (e)
{
closelog ();
openlog (e, LOG_PID, LOG_DAEMON);
vsyslog (level, fmt, ap);
}
}
static int _eindent (FILE *stream)
{
char *env = getenv ("RC_EINDENT");
int amount = 0;
char indent[INDENT_MAX];
if (env)
{
errno = 0;
amount = strtol (env, NULL, 0);
if (errno != 0 || amount < 0)
amount = 0;
else if (amount > INDENT_MAX)
amount = INDENT_MAX;
if (amount > 0)
memset (indent, ' ', amount);
}
/* Terminate it */
memset (indent + amount, 0, 1);
return (fprintf (stream, "%s", indent));
}
#define VEINFON(_file, _colour) \
if (colour_terminal ()) \
fprintf (_file, " " _colour "*" EINFO_NORMAL " "); \
else \
fprintf (_file, " * "); \
retval += _eindent (_file); \
retval += vfprintf (_file, fmt, ap) + 3; \
if (colour_terminal ()) \
fprintf (_file, "\033[K");
static int _veinfon (const char *fmt, va_list ap)
{
int retval = 0;
VEINFON (stdout, EINFO_GOOD);
return (retval);
}
static int _vewarnn (const char *fmt, va_list ap)
{
int retval = 0;
VEINFON (stdout, EINFO_WARN);
return (retval);
}
static int _veerrorn (const char *fmt, va_list ap)
{
int retval = 0;
VEINFON (stderr, EINFO_BAD);
return (retval);
}
int einfon (const char *fmt, ...)
{
int retval;
va_list ap;
if (! fmt || is_env ("RC_QUIET", "yes"))
return (0);
va_start (ap, fmt);
if (! (retval = ebuffer ("einfon", 0, fmt, ap)))
retval = _veinfon (fmt, ap);
va_end (ap);
return (retval);
}
int ewarnn (const char *fmt, ...)
{
int retval;
va_list ap;
if (! fmt || is_env ("RC_QUIET", "yes"))
return (0);
va_start (ap, fmt);
if (! (retval = ebuffer ("ewarnn", 0, fmt, ap)))
retval = _vewarnn (fmt, ap);
va_end (ap);
return (retval);
}
int eerrorn (const char *fmt, ...)
{
int retval;
va_list ap;
va_start (ap, fmt);
if (! (retval = ebuffer ("eerrorn", 0, fmt, ap)))
retval = _veerrorn (fmt, ap);
va_end (ap);
return (retval);
}
int einfo (const char *fmt, ...)
{
int retval;
va_list ap;
if (! fmt || is_env ("RC_QUIET", "yes"))
return (0);
va_start (ap, fmt);
if (! (retval = ebuffer ("einfo", 0, fmt, ap)))
{
retval = _veinfon (fmt, ap);
retval += printf ("\n");
}
va_end (ap);
return (retval);
}
int ewarn (const char *fmt, ...)
{
int retval;
va_list ap;
if (! fmt || is_env ("RC_QUIET", "yes"))
return (0);
va_start (ap, fmt);
elog (LOG_WARNING, fmt, ap);
if (! (retval = ebuffer ("ewarn", 0, fmt, ap)))
{
retval = _vewarnn (fmt, ap);
retval += printf ("\n");
}
va_end (ap);
return (retval);
}
void ewarnx (const char *fmt, ...)
{
int retval;
va_list ap;
if (fmt && ! is_env ("RC_QUIET", "yes"))
{
va_start (ap, fmt);
elog (LOG_WARNING, fmt, ap);
retval = _vewarnn (fmt, ap);
va_end (ap);
retval += printf ("\n");
}
exit (EXIT_FAILURE);
}
int eerror (const char *fmt, ...)
{
int retval;
va_list ap;
if (! fmt)
return (0);
va_start (ap, fmt);
elog (LOG_ERR, fmt, ap);
retval = _veerrorn (fmt, ap);
va_end (ap);
retval += fprintf (stderr, "\n");
return (retval);
}
void eerrorx (const char *fmt, ...)
{
va_list ap;
if (fmt)
{
va_start (ap, fmt);
elog (LOG_ERR, fmt, ap);
_veerrorn (fmt, ap);
va_end (ap);
printf ("\n");
}
exit (EXIT_FAILURE);
}
int ebegin (const char *fmt, ...)
{
int retval;
va_list ap;
if (! fmt || is_env ("RC_QUIET", "yes"))
return (0);
va_start (ap, fmt);
if ((retval = ebuffer ("ebegin", 0, fmt, ap)))
{
va_end (ap);
return (retval);
}
retval = _veinfon (fmt, ap);
va_end (ap);
retval += printf (" ...");
if (colour_terminal ())
retval += printf ("\n");
return (retval);
}
static void _eend (int col, einfo_color_t color, const char *msg)
{
FILE *fp = stdout;
int i;
int cols;
if (! msg)
return;
if (color == einfo_bad)
fp = stderr;
cols = get_term_columns () - (strlen (msg) + 6);
if (cols > 0 && colour_terminal ())
{
fprintf (fp, "\033[A\033[%dC %s[ ", cols, EINFO_BRACKET);
switch (color)
{
case einfo_good:
fprintf (fp, EINFO_GOOD);
break;
case einfo_warn:
fprintf (fp, EINFO_WARN);
break;
case einfo_bad:
fprintf (fp, EINFO_BAD);
break;
case einfo_hilite:
fprintf (fp, EINFO_HILITE);
break;
case einfo_bracket:
fprintf (fp, EINFO_BRACKET);
break;
case einfo_normal:
fprintf (fp, EINFO_NORMAL);
break;
}
fprintf (fp, "%s%s ]%s\n", msg, EINFO_BRACKET, EINFO_NORMAL);
}
else
{
for (i = -1; i < cols - col; i++)
fprintf (fp, " ");
fprintf (fp, "[ %s ]\n", msg);
}
}
static int _do_eend (const char *cmd, int retval, const char *fmt, va_list ap)
{
int col = 0;
FILE *fp;
if (ebuffer (cmd, retval, fmt, ap))
return (retval);
if (fmt && retval != 0)
{
if (strcmp (cmd, "ewend") == 0)
{
col = _vewarnn (fmt, ap);
fp = stdout;
}
else
{
col = _veerrorn (fmt, ap);
fp = stderr;
}
if (colour_terminal ())
fprintf (fp, "\n");
}
_eend (col, retval == 0 ? einfo_good : einfo_bad, retval == 0 ? OK : NOT_OK);
return (retval);
}
int eend (int retval, const char *fmt, ...)
{
va_list ap;
if (is_env ("RC_QUIET", "yes"))
return (retval);
va_start (ap, fmt);
_do_eend ("eend", retval, fmt, ap);
va_end (ap);
return (retval);
}
int ewend (int retval, const char *fmt, ...)
{
va_list ap;
if (is_env ("RC_QUIET", "yes"))
return (retval);
va_start (ap, fmt);
_do_eend ("ewend", retval, fmt, ap);
va_end (ap);
return (retval);
}
void ebracket (int col, einfo_color_t color, const char *msg)
{
_eend (col, color, msg);
}
void eindent (void)
{
char *env = getenv ("RC_EINDENT");
int amount = 0;
char num[10];
if (ebuffer ("eindent", 0, NULL, NULL))
return;
if (env)
{
errno = 0;
amount = strtol (env, NULL, 0);
if (errno != 0)
amount = 0;
}
amount += INDENT_WIDTH;
if (amount > INDENT_MAX)
amount = INDENT_MAX;
snprintf (num, 10, "%08d", amount);
setenv ("RC_EINDENT", num, 1);
}
void eoutdent (void)
{
char *env = getenv ("RC_EINDENT");
int amount = 0;
char num[10];
if (ebuffer ("eoutdent", 0, NULL, NULL))
return;
if (! env)
return;
errno = 0;
amount = strtol (env, NULL, 0);
if (errno != 0)
amount = 0;
else
amount -= INDENT_WIDTH;
if (amount <= 0)
unsetenv ("RC_EINDENT");
else
{
snprintf (num, 10, "%08d", amount);
setenv ("RC_EINDENT", num, 1);
}
}
int veinfon (const char *fmt, ...)
{
int retval;
va_list ap;
CHECK_VERBOSE;
if (! fmt)
return (0);
va_start (ap, fmt);
if (! (retval = ebuffer ("veinfon", 0, fmt, ap)))
retval = _veinfon (fmt, ap);
va_end (ap);
return (retval);
}
int vewarnn (const char *fmt, ...)
{
int retval;
va_list ap;
CHECK_VERBOSE;
if (! fmt)
return (0);
va_start (ap, fmt);
if (! (retval = ebuffer ("vewarnn", 0, fmt, ap)))
retval = _vewarnn (fmt, ap);
va_end (ap);
return (retval);
}
int veinfo (const char *fmt, ...)
{
int retval;
va_list ap;
CHECK_VERBOSE;
if (! fmt)
return (0);
va_start (ap, fmt);
if (! (retval = ebuffer ("veinfo", 0, fmt, ap)))
{
retval = _veinfon (fmt, ap);
retval += printf ("\n");
}
va_end (ap);
return (retval);
}
int vewarn (const char *fmt, ...)
{
int retval;
va_list ap;
CHECK_VERBOSE;
if (! fmt)
return (0);
va_start (ap, fmt);
if (! (retval = ebuffer ("vewarn", 0, fmt, ap)))
{
retval = _vewarnn (fmt, ap);
retval += printf ("\n");
}
va_end (ap);
retval += printf ("\n");
return (retval);
}
int vebegin (const char *fmt, ...)
{
int retval;
va_list ap;
CHECK_VERBOSE;
if (! fmt)
return (0);
va_start (ap, fmt);
if (! (retval = ebuffer ("vewarn", 0, fmt, ap)))
{
retval = _veinfon (fmt, ap);
retval += printf (" ...");
if (colour_terminal ())
retval += printf ("\n");
}
va_end (ap);
return (retval);
}
int veend (int retval, const char *fmt, ...)
{
va_list ap;
CHECK_VERBOSE;
va_start (ap, fmt);
_do_eend ("veend", retval, fmt, ap);
va_end (ap);
return (retval);
}
int vewend (int retval, const char *fmt, ...)
{
va_list ap;
CHECK_VERBOSE;
va_start (ap, fmt);
_do_eend ("vewend", retval, fmt, ap);
va_end (ap);
return (retval);
}
void veindent (void)
{
if (is_env ("RC_VERBOSE", "yes"))
eindent ();
}
void veoutdent (void)
{
if (is_env ("RC_VERBOSE", "yes"))
eoutdent ();
}