/*
** safeio.c - Signal/Async safe wrapper functions
**
** Copyright (c) 1997 Peter Eriksson <pen@lysator.liu.se>
**
** This program is free software; you can redistribute it and/or
** modify it as you wish - as long as you don't claim that you wrote
** it.
**
** 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.
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <syslog.h>

#include "pidentd.h"


void
s_abort(void)
{
    int *p = (int *) 0;

    *p = 4711;
    abort();
}



int
s_open(path, oflag, mode)
     const char *path;
     int oflag;
     int mode;
{
    int s;


    while ((s = open(path, oflag, mode)) < 0 && errno == EINTR)
	;
    
    if (s < 0 && (errno == EMFILE
		  || errno == ENFILE
		  || errno == ENOMEM 
#ifdef ENOSR
		  || errno == ENOSR
#endif
		  ))
    {
	/* Too many open files */
	
	syslog(LOG_WARNING, "s_open(\"%s\", 0%o): %m", path, oflag);
    }
    
    return s;
}



int
s_write(int fd, const char *buf, int len)
{
  int code;
  
  while ((code = write(fd, buf, len)) < 0 && errno == EINTR)
    ;
  return code;
}



int
s_read(int fd, char *buf, int len)
{
  int code;
  
  while ((code = read(fd, buf, len)) < 0 && errno == EINTR)
    ;
  return code;
}



int
s_close(int fd)
{
  int code;

  while ((code = close(fd)) < 0 && errno == EINTR)
    ;

  return code;
}



/*
** A "safe" malloc, that always succeeds (or logs an
** error to syslog and the exit(1)'s.
**
** The buffer returned is zeroed out.
*/
void *
s_malloc(int size)
{
    void *p;

    p = (void *) malloc(size);
    if (p == NULL)
    {
	if (debug)
	    fprintf(stderr, "s_malloc(%d) failed - aborting\n", size);
	
	syslog(LOG_ERR, "malloc(%d): %m", size);
	abort();
    }

    memset(p, 0, size);
    return p;
}


void
s_free(void *p)
{
    if (p)
	free(p);
}


char *
s_strdup(char *s)
{
    char *ns;
    int len;
    
    
    if (s == NULL)
	return NULL;

    len = strlen(s)+1;
    ns = (char *) malloc(len);
    if (ns == NULL)
    {
	syslog(LOG_ERR, "strdup(): malloc(%d): %m", len);
	exit(1);
    }

    memcpy(ns, s, len);
    return ns;
}


int
s_accept(int fd, struct sockaddr *sin, int *len)
{
    int new_fd;


    while ((new_fd = accept(fd, sin, len)) < 0 && errno == EINTR)
	;

    return new_fd;
}


int
s_getsockname(int fd, struct sockaddr *sin, int *len)
{
    int code;


    while ((code = getsockname(fd, sin, len)) < 0 && errno == EINTR)
	;

    return code;
}


int
s_getpeername(int fd, struct sockaddr *sin, int *len)
{
    int code;


    while ((code = getpeername(fd, sin, len)) < 0 && errno == EINTR)
	;

    return code;
}



static pthread_mutex_t random_lock;
static pthread_once_t random_once = PTHREAD_ONCE_INIT;

static void random_lock_init(void)
{
    unsigned int seed;
    
    pthread_mutex_init(&random_lock, NULL);
    
    time((time_t *) &seed);
#ifdef HAVE_SRANDOM
    srandom(seed);
#else
    srand(seed);
#endif
}


long s_random(void)
{
    long res;
    
    pthread_once(&random_once, random_lock_init);
    
    pthread_mutex_lock(&random_lock);
#ifdef HAVE_RANDOM
    res = random();
#else
    res = rand();
#endif
    pthread_mutex_unlock(&random_lock);

    return res;
}
