Logo Search packages:      
Sourcecode: darkstat version File versions  Download package

conv.c

/* darkstat 3
 * copyright (c) 2001-2007 Emil Mikulic.
 *
 * conv.c: convenience functions.
 *
 * Permission to use, copy, modify, and distribute this file for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "darkstat.h"
#include "conv.h"

#include <sys/wait.h>
#include <assert.h>
#include <ctype.h>
#include "err.h"
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#define PATH_DEVNULL "/dev/null"

/* malloc() that exits on failure. */
void *
xmalloc(const size_t size)
{
   void *ptr = malloc(size);

   if (ptr == NULL)
      errx(1, "malloc(): out of memory");
   return (ptr);
}

/* calloc() that exits on failure. */
void *
xcalloc(const size_t num, const size_t size)
{
   void *ptr = calloc(num, size);

   if (ptr == NULL)
      errx(1, "calloc(): out of memory");
   return (ptr);
}

/* realloc() that exits on failure. */
void *
xrealloc(void *original, const size_t size)
{
    void *ptr = realloc(original, size);

    if (ptr == NULL)
      errx(1, "realloc(): out of memory");
    return (ptr);
}

/* strdup() that exits on failure. */
char *
xstrdup(const char *s)
{
   char *tmp = strdup(s);

   if (tmp == NULL)
      errx(1, "strdup(): out of memory");
   return (tmp);
}

/* ---------------------------------------------------------------------------
 * Split string out of src with range [left:right-1]
 */
char *
split_string(const char *src, const size_t left, const size_t right)
{
    char *dest;
    assert(left <= right);
    assert(left < strlen(src));   /* [left means must be smaller */
    assert(right <= strlen(src)); /* right) means can be equal or smaller */

    dest = xmalloc(right - left + 1);
    memcpy(dest, src+left, right-left);
    dest[right-left] = '\0';
    return (dest);
}

/* ---------------------------------------------------------------------------
 * Uppercasify all characters in a string of given length.
 */
void
strntoupper(char *str, const size_t length)
{
    size_t i;

    for (i=0; i<length; i++)
        str[i] = toupper(str[i]);
}

/* ---------------------------------------------------------------------------
 * Returns non-zero if haystack starts with needle.
 */
int
str_starts_with(const char *haystack, const char *needle)
{
   int i = 0;

   while (needle[i] != '\0') {
      if ((haystack[i] == '\0') || (haystack[i] != needle[i]))
         return (0);
      i++;
   }
   return (1);
}

/* split - splits a string by a delimiter character into an array of
 * string chunks.
 *
 * The chunks and the array are dynamically allocated using xmalloc() so
 * it will errx() if it runs out of memory.
 *
 *    int num_chunks;
 *    char **chunks = split('.', "..one...two....", &num_chunks);
 *
 *    num_chunks = 2, chunks = { "one", "two", NULL }
 */
char **
split(const char delimiter, const char *str, int *num_chunks)
{
   int num = 0;
   char **chunks = NULL;
   size_t left, right = 0;

   #define PUSH(c) do { num++;  chunks = (char**) xrealloc(chunks, \
      sizeof(*chunks) * num);  chunks[num-1] = c; } while(0)

   for(;;) {
      /* find first non-delimiter */
      for (left = right; str[left] == delimiter; left++)
            ;

      if (str[left] == '\0')
         break; /* ran out of string */

      /* find first delimiter or end of string */
      for (right=left+1;
         str[right] != delimiter && str[right] != '\0';
         right++)
            ;

      /* split chunk out */
      PUSH( split_string(str, left, right) );

      if (str[right] == '\0')
         break; /* ran out of string */
      else
         right++;
   }

   /* return */
   PUSH(NULL);
   if (num_chunks != NULL)
      *num_chunks = num-1; /* NULL doesn't count */
   return (chunks);
   #undef PUSH
}

/* Given an HTTP query string and a key to search for, return the value
 * associated with it, or NULL if there is no such key or qs is NULL.
 * The returned string needs to be freed.
 *
 * e.g.:
 * qs = "sort=in&start=20";
 * qs_get(sq, "sort") returns "in"
 * qs_get(sq, "end") returns NULL
 */
char *
qs_get(const char *qs, const char *key)
{
   size_t pos, qslen, keylen;

   if (qs == NULL) return NULL;

   qslen = strlen(qs);
   keylen = strlen(key);
   pos = 0;
   while (pos < qslen) {
      if (!(pos + keylen + 1 < qslen))
         /* not enough room for "key" + "=" */
         return NULL;
      else {
         if (str_starts_with(qs+pos, key) && qs[pos+keylen] == '=') {
            /* found key= */
            size_t start, end;

            start = pos + keylen + 1;
            for (end=start; end<qslen && qs[end] != '&'; end++)
               ;
            return split_string(qs, start, end);
         } else {
            /* didn't find key, skip to next & */
            do { pos++; } while ((pos < qslen) && (qs[pos] != '&'));
            pos++; /* skip the ampersand */
         }
      }
   }
   return NULL; /* not found */
}

static int lifeline[2] = { -1, -1 };
static int fd_null = -1;

void
daemonize_start(void)
{
   pid_t f, w;

   if (pipe(lifeline) == -1)
      err(1, "pipe(lifeline)");

   fd_null = open(PATH_DEVNULL, O_RDWR, 0);
   if (fd_null == -1)
      err(1, "open(" PATH_DEVNULL ")");

   f = fork();
   if (f == -1)
      err(1, "fork");
   else if (f != 0) {
      /* parent: wait for child */
      char tmp[1];
      int status;

      verbosef("parent waiting");
      if (close(lifeline[1]) == -1)
         warn("close lifeline in parent");
      read(lifeline[0], tmp, sizeof(tmp));
      verbosef("parent done reading, calling waitpid");
      w = waitpid(f, &status, WNOHANG);
      verbosef("waitpid ret %d, status is %d", w, status);
      if (w == -1)
         err(1, "waitpid");
      else if (w == 0)
         /* child is running happily */
         exit(EXIT_SUCCESS);
      else
         /* child init failed, pass on its exit status */
         exit(WEXITSTATUS(status));
   }
   /* else we are the child: continue initializing */
}

void
daemonize_finish(void)
{
   if (fd_null == -1)
      return; /* didn't daemonize_start(), i.e. we're not daemonizing */

   if (setsid() == -1)
      err(1, "setsid");
   if (close(lifeline[0]) == -1)
      warn("close read end of lifeline in child");
   if (close(lifeline[1]) == -1)
      warn("couldn't cut the lifeline");

   /* close all our std fds */
   if (dup2(fd_null, STDIN_FILENO) == -1)
      warn("dup2(stdin)");
   if (dup2(fd_null, STDOUT_FILENO) == -1)
      warn("dup2(stdout)");
   if (dup2(fd_null, STDERR_FILENO) == -1)
      warn("dup2(stderr)");
   if (fd_null > 2)
      close(fd_null);
}

/*
 * For security, chroot (optionally) and drop privileges.
 * Pass a NULL chroot_dir to disable chroot() behaviour.
 */
void
privdrop(const char *chroot_dir, const char *privdrop_user)
{
   struct passwd *pw;

   errno = 0;
   pw = getpwnam(privdrop_user);

   if (pw == NULL) {
      if (errno == 0)
         errx(1, "getpwnam(\"%s\") failed: no such user", privdrop_user);
      else
         err(1, "getpwnam(\"%s\") failed", privdrop_user);
   }
   if (chroot_dir != NULL) {
      tzset(); /* read /etc/localtime before we chroot */
      if (chdir(chroot_dir) == -1)
         err(1, "chdir(\"%s\") failed", chroot_dir);
      if (chroot(chroot_dir) == -1)
         err(1, "chroot(\"%s\") failed", chroot_dir);
      verbosef("chrooted into: %s", chroot_dir);
   }
   if (setgid(pw->pw_gid) == -1)
      err(1, "setgid");
   if (setuid(pw->pw_uid) == -1)
      err(1, "setuid");
   verbosef("set uid/gid to %d/%d", (int)pw->pw_uid, (int)pw->pw_gid);
}

/* Make the specified file descriptor non-blocking. */
void
fd_set_nonblock(const int fd)
{
   int flags;

   if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
      err(1, "fcntl(fd %d) to get flags", fd);
   flags |= O_NONBLOCK;
   if (fcntl(fd, F_SETFL, flags) == -1)
      err(1, "fcntl(fd %d) to set O_NONBLOCK", fd);
   assert( (fcntl(fd, F_GETFL, 0) & O_NONBLOCK ) == O_NONBLOCK );
}

/* Make the specified file descriptor blocking. */
void
fd_set_block(const int fd)
{
   int flags;

   if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
      err(1, "fcntl(fd %d) to get flags", fd);
   flags &= ~O_NONBLOCK;
   if (fcntl(fd, F_SETFL, flags) == -1)
      err(1, "fcntl(fd %d) to unset O_NONBLOCK", fd);
   assert( (fcntl(fd, F_GETFL, 0) & O_NONBLOCK ) == 0 );
}

/* strlcpy() and strlcat() are derived from:
 *
 * $OpenBSD: strlcpy.c,v 1.4
 * $FreeBSD: src/lib/libc/string/strlcpy.c,v 1.8
 *
 * $OpenBSD: strlcat.c,v 1.2
 * $FreeBSD: src/lib/libc/string/strlcat.c,v 1.10
 *
 * under the following license:
 *
 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef HAVE_STRLCPY
/*
 * Copy src to string dst of size siz.  At most siz-1 characters
 * will be copied.  Always NUL terminates (unless siz == 0).
 * Returns strlen(src); if retval >= siz, truncation occurred.
 */
size_t strlcpy(dst, src, siz)
        char *dst;
        const char *src;
        size_t siz;
{
        char *d = dst;
        const char *s = src;
        size_t n = siz;

        /* Copy as many bytes as will fit */
        if (n != 0 && --n != 0) {
                do {
                        if ((*d++ = *s++) == 0)
                                break;
                } while (--n != 0);
        }

        /* Not enough room in dst, add NUL and traverse rest of src */
        if (n == 0) {
                if (siz != 0)
                        *d = '\0';              /* NUL-terminate dst */
                while (*s++)
                        ;
        }

        return(s - src - 1);    /* count does not include NUL */
}
#endif

#ifndef HAVE_STRLCAT
/*
 * Appends src to string dst of size siz (unlike strncat, siz is the
 * full size of dst, not space left).  At most siz-1 characters
 * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
 * Returns strlen(src) + MIN(siz, strlen(initial dst)).
 * If retval >= siz, truncation occurred.
 */
size_t
strlcat(dst, src, siz)
        char *dst;
        const char *src;
        size_t siz;
{
        char *d = dst;
        const char *s = src;
        size_t n = siz;
        size_t dlen;

        /* Find the end of dst and adjust bytes left but don't go past end */
        while (n-- != 0 && *d != '\0')
                d++;
        dlen = d - dst;
        n = siz - dlen;

        if (n == 0)
                return(dlen + strlen(s));
        while (*s != '\0') {
                if (n != 1) {
                        *d++ = *s;
                        n--;
                }
                s++;
        }
        *d = '\0';

        return(dlen + (s - src));       /* count does not include NUL */
}
#endif

/* vim:set ts=3 sw=3 tw=78 expandtab: */

Generated by  Doxygen 1.6.0   Back to index