/*
 * Secure X Message Passing Library v2 examples.
 *
 * (c) Originally written by somebody else ...
 * (c) Alexander Vdolainen 2013-2015 <avdolainen@gmail.com>
 *
 * libsxmp is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libsxmp 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.";
 *
 * This is a simple helpers to make code more clean;
 *
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/types.h>
#include <limits.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/resource.h>
#include <netdb.h>
#include <unistd.h>
#include <netinet/in.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <syslog.h>

int openlistener_socket(int port)
{
  int sd;
  struct sockaddr_in addr;

  sd = socket(PF_INET, SOCK_STREAM, 0);
  memset(&addr, 0, sizeof(addr));

  addr.sin_family = AF_INET;
  addr.sin_port = htons(port);
  addr.sin_addr.s_addr = INADDR_ANY;

  if(bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
    perror("can't bind port");
    abort();
  }
  if(listen(sd, 10) != 0) {
    perror("Can't configure listening port");
    abort();
  }

  return sd;
}

char *nodehostname(void)
{
  char *hm = malloc(HOST_NAME_MAX);

  if(!gethostname(hm, HOST_NAME_MAX))
    return hm;
  else {
    if(hm) free(hm);
    return NULL;
  }
}

static inline int __removechar(char *b, size_t chrs)
{
  char *c = b + chrs;
  size_t blen = strlen(c) + sizeof(char);

  memmove(b, c, blen);

  return chrs;
}

int normalize_path(char *path)
{
  char *obuf = path;
  int cutlen;

  /* first remove useless / if found */
  while((obuf = strchr(obuf, '/'))) {
    obuf++;
    while(*obuf == '/') __removechar(obuf, 1);
  }

  obuf = path; /* much cleaner now */
  while(*obuf != '\0') {
    if(*obuf == '.' && *(obuf - 1) == '/') {
      if(!strcmp(obuf, "./")) __removechar(obuf, 2);
      else if(!strcmp(obuf, "../")) {
        cutlen = strlen(path) - strlen(obuf);
        if(cutlen < 4) return -1;
        else cutlen = 4;
        obuf -= 2;
        while(*obuf != '/') {
          obuf--; cutlen++;
        }
        __removechar(obuf, cutlen);
      }
    }
    obuf++;
  }

  return 0;
}

/* syslog stuff */

static size_t writer(void *cookie, char const *data, size_t len)
{
  (void)cookie;
  syslog(LOG_DEBUG, "%.*s", (int)len, data);
  return len;
}
static int noop(void) { return 0; }

static cookie_io_functions_t log_fns = {
  (void*) noop, (void*) writer, (void*) noop, (void*) noop
};

void tosyslog(FILE **pfp)
{
  setvbuf(*pfp = fopencookie(NULL, "w", log_fns), NULL, _IOLBF, 0);
}

/* daemonization stuff */

#define DM_MAX_CLOSE 8192

int daemonize(void)
{
  pid_t pid;

  /* fork */
  pid = fork();
  if (pid == -1) {
    perror("fork() failed");
    return EFAULT;
  }
  else if (pid != 0) {
    exit(EXIT_SUCCESS);
  }

  /* create new session and progress group */
  if (setsid() == -1) {
    perror("setsid() failed");
    return EFAULT;
  }

  /* set the working directory to the root directory */
  if (chdir("/") == -1) {
    perror("chdir() failed");
    return EFAULT;
  }

  /* close all open files */
  int fd;
  int maxfd = sysconf(_SC_OPEN_MAX);
  if (maxfd == -1)
    maxfd = DM_MAX_CLOSE;   /* if this limit is indeterminate, take a guess */
  for (fd = 0; fd < maxfd; fd++)
    close(fd);

  /* reopen standard fd's /dev/null */
  close(STDIN_FILENO);
  fd = open("/dev/null", O_RDWR);
  if (fd != STDIN_FILENO) {           /* fd should be 0 */
    return EBADFD;
  }
  if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO) {
    return EBADFD;
  }
  if (dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO) {
    return EBADFD;
  }

  /* redirect output to syslog as well */
  tosyslog(&stdout);
  tosyslog(&stderr);

  return 0;
}