/*
 * Copyright (C) 2003  Sam Horrocks
 * 
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

#include "perperl.h"

void perperl_group_invalidate(slotnum_t gslotnum) {
    gr_slot_t *gslot = &FILE_SLOT(gr_slot, gslotnum);

    /* Don't invalidate twice */
    if (!perperl_group_isvalid(gslotnum))
	return;

    /* Remove scripts from the script list */
    {
	slotnum_t snum, next;

	for (snum = gslot->script_head; snum; snum = next) {
	    next = perperl_slot_next(snum);
	    SLOT_FREE(snum, "script (perperl_group_invalidate)");
	}
	gslot->script_head = 0;
    }

    /* Remove the group name if any */
    if (gslot->name_slot) {
	SLOT_FREE(gslot->name_slot, "name (perperl_group_invalidate)");
	gslot->name_slot = 0;
    }

    /* Remove backends from the be_wait queue */
    perperl_backend_remove_be_wait(gslotnum);

    /* Move this group to the tail of the group list */
    perperl_slot_move_tail(gslotnum,
	&(FILE_HEAD.group_head), &(FILE_HEAD.group_tail));
}

pid_t perperl_group_be_starting(slotnum_t gslotnum) {
    gr_slot_t *gslot = &FILE_SLOT(gr_slot, gslotnum);
    pid_t be_pid = gslot->be_starting;

    if (be_pid) {
	if (perperl_util_kill(be_pid, 0) != -1)
	    return be_pid;
	gslot->be_starting = 0;
    }
    return 0;
}

void perperl_group_sendsigs(slotnum_t gslotnum) {
    gr_slot_t *gslot = &FILE_SLOT(gr_slot, gslotnum);
    slotnum_t fslotnum, bslotnum;

    /* Get first slot in the fe list */
    fslotnum = gslot->fe_head;

    /* Loop over each backend slot in the wait list */
    for (bslotnum = gslot->be_head;
	 bslotnum && fslotnum && !FILE_SLOT(be_slot, bslotnum).fe_running;
         bslotnum = perperl_slot_next(bslotnum))
    {
	slotnum_t next;

	for (; fslotnum; fslotnum = next) {
	    /* Get next FE */
	    fe_slot_t *fslot = &FILE_SLOT(fe_slot, fslotnum);
	    next = perperl_slot_next(fslotnum);

	    /* If it's not us send an ALRM signal */
	    if (perperl_util_kill(fslot->pid, SIGALRM) != -1) {
		fslot->sent_sig = 1;
		break;
	    }

	    /* Failed, remove this FE and try again */
	    perperl_frontend_dispose(gslotnum, fslotnum);
	}

	/* Only wake up one FE at a time.. */
	break;
    }
}

/* Cleanup this group after an fe/be has been removed */
void perperl_group_cleanup(slotnum_t gslotnum) {

    /* No cleanup if there are still be's or fe's */
    if (FILE_SLOT(gr_slot, gslotnum).be_head ||
        FILE_SLOT(gr_slot, gslotnum).fe_head)
    {
	return;
    }

    /* Kill the parent */
    perperl_util_kill(FILE_SLOT(gr_slot, gslotnum).be_parent, SIGKILL);

    /* Invalidate - cleans up resources belonging to the group */
    perperl_group_invalidate(gslotnum);

    /* Remove our group from the list */
    perperl_slot_remove(gslotnum, &(FILE_HEAD.group_head), &(FILE_HEAD.group_tail));

    SLOT_FREE(gslotnum, "group (perperl_group_cleanup)");
}

slotnum_t perperl_group_create(void) {
    slotnum_t gslotnum;

    gslotnum = SLOT_ALLOC("group (perperl_group_create)");

    perperl_slot_insert(gslotnum, &(FILE_HEAD.group_head), &(FILE_HEAD.group_tail));

    if (!DOING_SINGLE_SCRIPT) {
	register slotnum_t nslotnum;

	nslotnum = SLOT_ALLOC("name (perperl_group_create)");
	FILE_SLOT(gr_slot, gslotnum).name_slot = nslotnum;
	strncpy(FILE_SLOT(grnm_slot, nslotnum).name, OPTVAL_GROUP, GR_NAMELEN);
    }
    return gslotnum;
}


int perperl_group_parent_sig(slotnum_t gslotnum, int sig) {
    gr_slot_t *gslot = &FILE_SLOT(gr_slot, gslotnum);

    if (perperl_util_kill(gslot->be_parent, sig) == -1) {
	perperl_group_invalidate(gslotnum);
	gslot->be_parent = 0;
	return 0;
    }
    return 1;
}

int perperl_group_start_be(slotnum_t gslotnum) {
    gr_slot_t *gslot = &FILE_SLOT(gr_slot, gslotnum);

    /* If the parent is still starting up, then consider it signalled */
    if (gslot->be_parent && gslot->be_parent == gslot->be_starting)
	return 1;
    
    return perperl_group_parent_sig(gslotnum, SIGUSR1);
}

int perperl_group_lock(slotnum_t gslotnum) {
    perperl_file_set_state(FS_CORRUPT);
    return perperl_group_isvalid(gslotnum);
}