/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include "signalmap.h"

static void signal_init(void);
#pragma init(signal_init)

extern void (*handlers[])();
extern void maphandler(int, int, struct sigcontext *, char *);
extern void (*_siguhandler[])();		/* libucb */
extern void _sigvechandler(int, void*, void*);	/* libucb */

extern int maptonewsig();
extern int _sigaction();
extern int maptonewmask();
extern int maptooldmask();
extern int _signal();
extern int _sigprocmask();
extern char *memset();
extern int _sigpending();

typedef struct {
	unsigned long	__sigbits[4];
} S5_sigset_t;

typedef struct {
	int		sa_flags;
	void		(*sa_handler)();
	S5_sigset_t	sa_mask;
	int		sa_resv[2];
} S5_sigaction;

#define	S5_SA_ONSTACK	0x00000001
#define	S5_SA_RESETHAND	0x00000002
#define	S5_SA_RESTART	0x00000004
#define	S5_SA_NOCLDSTOP	0x00020000

int
sigaction(sig, act, oact)
int sig;
struct sigaction *act, *oact;
{
	S5_sigaction	S5_act;
	S5_sigaction	S5_oact;
	int		ret;
	int		newsig;
	void		(*oldhand)();
	void		(*oldsiguhand)();

	newsig = maptonewsig(sig);
	oldhand = handlers[newsig];
	oldsiguhand = _siguhandler[newsig];
	if (act == NULL) {
		ret = _sigaction(newsig, (S5_sigaction *)NULL, &S5_oact);
	} else {
		S5_act.sa_flags = 0;
		if (act->sa_flags & SA_ONSTACK)
			S5_act.sa_flags |= S5_SA_ONSTACK;
		if (act->sa_flags & SA_RESETHAND)
			S5_act.sa_flags |= S5_SA_RESETHAND;
		if (act->sa_flags & SA_NOCLDSTOP)
			S5_act.sa_flags |= S5_SA_NOCLDSTOP;
		if (!(act->sa_flags & SA_INTERRUPT))
			S5_act.sa_flags |= S5_SA_RESTART;
		/*
		 * _sigvechandler() receives control from the OS.
		 * It calls through _siguhandler[] to maphandler(),
		 * which maps the signal number new-to-old, and
		 * calls the user's handler through handlers[].
		 */
		handlers[newsig] = act->sa_handler;
		_siguhandler[newsig] = maphandler;
		if ((act->sa_handler == SIG_DFL) ||
		    (act->sa_handler == SIG_IGN))
			S5_act.sa_handler = act->sa_handler;
		else
			S5_act.sa_handler = _sigvechandler;
		S5_act.sa_mask.__sigbits[0] = maptonewmask(act->sa_mask);
		S5_act.sa_mask.__sigbits[1] = 0;
		S5_act.sa_mask.__sigbits[2] = 0;
		S5_act.sa_mask.__sigbits[3] = 0;

		ret = _sigaction(newsig, &S5_act, &S5_oact);
	}

	if ((oact != NULL) && (ret != -1)) {
		oact->sa_flags = 0;
		if (S5_oact.sa_flags & S5_SA_ONSTACK)
			oact->sa_flags |= SA_ONSTACK;
		if (S5_oact.sa_flags & S5_SA_RESETHAND)
			oact->sa_flags |= SA_RESETHAND;
		if (S5_oact.sa_flags & S5_SA_NOCLDSTOP)
			oact->sa_flags |= SA_NOCLDSTOP;
		if (!(S5_oact.sa_flags & S5_SA_RESTART))
			oact->sa_flags |= SA_INTERRUPT;
		if ((S5_oact.sa_handler == SIG_DFL) ||
		    (S5_oact.sa_handler == SIG_IGN))
			oact->sa_handler = S5_oact.sa_handler;
		else
			oact->sa_handler = oldhand;
		oact->sa_mask = maptooldmask(S5_oact.sa_mask.__sigbits[0]);
	}
	if (ret == -1) {
		handlers[newsig] = oldhand;
		_siguhandler[newsig] = oldsiguhand;
	}
	return (ret);
}

static void
signal_init() {
#define	S5_SIGPOLL	22
	_signal(S5_SIGPOLL, SIG_IGN);
#undef S5_SIGPOLL
}

int
sigprocmask(how, set, oset)
int how;
sigset_t *set, *oset;
{
	int		how_map[] = {0, 1, 2, 0, 3};
	int		ret;
	S5_sigset_t	s5_set, s5_oset;

	if (set == NULL) /* query */
		ret = _sigprocmask(how_map[how], NULL, &s5_oset);
	else {
		memset(&s5_set, 0, sizeof (S5_sigset_t));
		s5_set.__sigbits[0] = maptonewmask(*set);
		ret = _sigprocmask(how_map[how], &s5_set, &s5_oset);
	}
	if ((oset != NULL) && (ret == 0))
		*oset = maptooldmask(s5_oset.__sigbits[0]);
	return (ret);
}

int
sigpending(set)
sigset_t *set;
{
	S5_sigset_t	s5_set;
	int		ret;

	ret = _sigpending(&s5_set);
	*set = maptooldmask(s5_set.__sigbits[0]);
	return (ret);
}