/*
  utils.c - Some useful things
  
  (c) 1999 Robert Cheramy <tibob@via.ecp.fr>
  (c) 2000 Samuel Hocevar <sam@via.ecp.fr>
  (c) 2001 Loïc Tortay & IN2P3 Computing Center <tortay@cc.in2p3.fr>

  200010xx : sam : makedirname & pid file handling
 */

/*
 *  This program 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 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#include "config.h"
#include "utils.h"
#include "missing/missing.h"

extern ipfm_timezone tz;

char *timefile(char *filemask, time_t when) {
  char s_temp[FILENAME_MAX];
  char *s_cpy;
  struct tm *p_time;
  
  if (tz == local) {
    p_time = localtime(&when);
  } else {
    p_time = gmtime(&when);
  }
  strftime(s_temp, sizeof(s_temp), filemask, p_time);

  s_cpy = xstrdup(s_temp);
  return (s_cpy);
}

int makedirname(char *filename) {
  static struct stat statbuf;
  char *s_temp = filename;
  char *s_slash = NULL;

  /* check in which directory `filename' is going to be,
   * so that we can get /foo/bar from /foo/bar/baz */
  while (*s_temp) {
    if('/' == *s_temp) {
      s_slash = s_temp;
    }
    s_temp++;
  }

  if (NULL == s_slash || s_slash == filename) {
    /* either `filename' is a relative path (pwd is a dir), or
     * `filename' is directly in /, which is a directory as well.
     * In both cases, return OK */
    return(0);
  }
  /* /foo/bar/baz becomes /foo/bar and we check if /foo/bar is a
   * directory, which is required to create baz afterwards */
  *s_slash = '\0';
  /* Check if /foo/bar is already a directory */
  if (-1 != stat(filename, &statbuf)) {
    if (S_ISDIR(statbuf.st_mode)) {
      *s_slash = '/';
      return(0);
    }
  }
  /* Check if we can create /foo/bar (recursive call to check /foo) */
  if (-1 != makedirname (filename)) {
    if (-1 != mkdir(filename, DEFAULT_DIR_MODE)) {
      *s_slash = '/';
      return(0);
    }
  }
  /* Everything failed, we give up */
  *s_slash = '/';
  return(-1);
}

/*
  Borrowed from IPPL by Hugo Haas and Etienne Bernard
  http://pltplp.net/ippl/
*/
int check_pid (char *pidfile)
{
  pid_t pid = read_pid(pidfile);

  if ((!pid) || (pid == getpid ()))
    return 0;

  if (kill(pid, 0) && (ESRCH == errno)) {
    return 0;
  }

  return pid;
}

int read_pid (char *pidfile)
{
  FILE *f;
  int pid;

  if (!(f = fopen(pidfile,"r"))) {
    return 0;
  }
  fscanf (f, "%d", &pid);
  fclose (f);

  return pid;
}

int write_pid (char *pidfile)
{
  FILE *f;
  int fd;
  pid_t pid;

  if (-1 == ((fd = open(pidfile, O_RDWR | O_CREAT, 0644)))
      || (NULL == (f = fdopen(fd, "r+"))) ) {
    fprintf (stderr, "Can't open or create %s.\n", pidfile);
    return 0;
  }

#ifdef HAVE_LOCKF
  if (-1 == lockf(fd, F_TLOCK, 0)) {
#elif HAVE_FLOCK
  if (-1 == flock(fd, LOCK_EX|LOCK_NB)) {
#endif
    fscanf (f, "%u", &pid);
    fclose (f);
    printf ("Can't lock, lock is held by pid %u.\n", pid);
    return 0;
  }

  pid = getpid();
  if (!fprintf(f, "%u\n", pid)) {
    printf ("Can't write pid , %s.\n", strerror(errno));
    close (fd);
    return 0;
  }
  fflush(f);

#ifdef HAVE_LOCKF
  if (-1 == lockf(fd, F_ULOCK, 0)) {
#elif HAVE_FLOCK
   if (-1 == flock(fd, LOCK_UN)) {
#endif
    printf ("Can't unlock pidfile %s, %s.\n", pidfile, strerror(errno));
    close (fd);
    return 0;
  }
  close (fd);

  return pid;
}

int remove_pid (char *pidfile) {
  return unlink (pidfile);
}

void *xmalloc(size_t size) {
  void *p = malloc(size);

  if (p == NULL) {
    fprintf(stderr, "Unsufficient memory, malloc failed\n");
    exit(2);
  }

  return p;
}

void xfree(void *p) {
  if (p == NULL) {
    fprintf(stderr, "Trying to free a NULL pointer\n");
    exit(2);
  }

  free(p);
}

char *xstrdup(const char *s1) {
  int len = strlen(s1) + 1;
  char *cp = xmalloc(len);

  strlcpy(cp, s1, len);

  return cp;
}

