/*
 * Copyright (C) 1994-2018 Altair Engineering, Inc.
 * For more information, contact Altair at www.altair.com.
 *
 * This file is part of the PBS Professional ("PBS Pro") software.
 *
 * Open Source License Information:
 *
 * PBS Pro is free software. You can redistribute it and/or modify it under the
 * terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * PBS Pro 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Commercial License Information:
 *
 * For a copy of the commercial license terms and conditions,
 * go to: (http://www.pbspro.com/UserArea/agreement.html)
 * or contact the Altair Legal Department.
 *
 * Altair’s dual-license business model allows companies, individuals, and
 * organizations to create proprietary derivative works of PBS Pro and
 * distribute them - whether embedded or bundled with other software -
 * under a commercial license agreement.
 *
 * Use of Altair’s trademarks, including but not limited to "PBS™",
 * "PBS Professional®", and "PBS Pro™" and Altair’s logos is subject to Altair's
 * trademark licensing policies.
 *
 */

/**
 * @file    queue_recov.c
 *
 * @brief
 *		queue_recov.c - This file contains the functions to record a queue
 *		data struture to disk and to recover it from disk.
 *
 *		The data is recorded in a file whose name is the queue name
 *
 *	The following public functions are provided:
 *		que_save_fs()  - save the disk image
 *		que_recov_fs()  - recover (read) queue from disk
 */

#include <pbs_config.h>   /* the master config generated by configure */

#include <sys/types.h>
#include <sys/param.h>
#include "pbs_ifl.h"
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "server_limits.h"
#include "list_link.h"
#include "attribute.h"
#include "job.h"
#include "reservation.h"
#include "queue.h"
#include "log.h"
#include "pbs_nodes.h"
#include "svrfunc.h"


/* data global to this file */

extern char *path_queues;
extern char  pbs_recov_filename[];

/**
 * @brief
 *		Saves a queue structure image on disk
 *
 *		For a save, to insure no data is ever lost due to system crash:
 *		1. write (with O_SYNC) new image to a new file using a temp name
 *		2. rename the temp file to the old
 *
 *		Then, if the queue has any access control lists, they are saved
 *		to their own files.
 *
 * @param[in]	pque - Pointer to queue structure to save
 *
 * @return      Error code
 * @retval	0	- Success
 * @retval	-1  - Failure
 */

int
que_save_fs(pbs_queue	*pque)
{
	int	fds;
	int	i;
	char	namebuf1[MAXPATHLEN];
	char	namebuf2[MAXPATHLEN];

	(void)strcpy(namebuf1, path_queues);
	(void)strcat(namebuf1, pque->qu_qs.qu_name);
	(void)strcpy(namebuf2, namebuf1);
	(void)strcat(namebuf2, ".new");

#ifdef WIN32
	fix_perms2(namebuf2, namebuf1);
#endif

	fds = open(namebuf2, O_CREAT | O_WRONLY | O_Sync, 0600);
	if (fds < 0) {
		sprintf(log_buffer, "error opening %s", namebuf2);
		log_err(errno, __func__, log_buffer);
		return (-1);
	}
#ifdef WIN32
	secure_file(namebuf2, "Administrators", READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED);
	setmode(fds, O_BINARY);
#endif

	/* set up save buffering system */

	save_setup(fds);

	/* save basic queue structure (fixed length stuff) */

	if (save_struct((char *)&pque->qu_qs, sizeof(struct queuefix)) != 0) {
		log_err(-1, __func__, "save_struct failed");
		(void)close(fds);
		return (-1);
	}

	/* save queue attributes  */

	if (save_attr_fs(que_attr_def, pque->qu_attr, (int)QA_ATR_LAST) != 0) {
		log_err(-1, __func__, "save_attr failed");
		(void)close(fds);
		return (-1);
	}

	if (save_flush() != 0) {	/* flush the save buffer */
		log_err(-1, __func__, "save_flush failed");
		(void)close(fds);
		return (-1);
	}

#ifdef WIN32
	if (_commit(fds) != 0) {
		log_err(errno, __func__, "flush queue file to disk failed!");
		close(fds);
		return (-1);
	}
#endif
	(void)close(fds);

#ifdef WIN32
	if (MoveFileEx(namebuf2, namebuf1,
		MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) == 0) {

		errno = GetLastError();
		sprintf(log_buffer, "MoveFileEx(%s, %s) failed!",
			namebuf2, namebuf1);
		log_err(errno, __func__, log_buffer);
	}
	secure_file(namebuf1, "Administrators",
		READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED);
#else
	if (rename(namebuf2, namebuf1) < 0)
		log_err(errno, __func__, "unable to rename queue name");
#endif

	/*
	 * now search queue's attributes for access control lists,
	 * they are saved separately in their own file:
	 * ../priv/(attr name)/(queue name)
	 */

	for (i=0; i < (int)QA_ATR_LAST; i++) {
		if (pque->qu_attr[i].at_type == ATR_TYPE_ACL) {
			save_acl(&pque->qu_attr[i], &que_attr_def[i],
				que_attr_def[i].at_name, pque->qu_qs.qu_name);
		}
	}

	return (0);
}

/**
 * @brief
 *		Load (recover) a queue from its save file
 *
 *		This function is only needed upon server start up.
 *
 *		The queue structure is recovered from the disk.
 *		Space to hold the above is malloc-ed as needed.
 *
 * @param[in]	filename	- Name of file to load queue from
 *
 * @return	pointer to new queue structure
 * @retval	NULL	- Failure
 * @retval	!NULL	- Success
 *
 */

pbs_queue *
que_recov_fs(char   *filename)
{
	int	   fds;
	int	   i;
	pbs_queue *pq;


	pq = que_alloc(filename);  /* allocate & init queue structure space */
	if (pq == NULL) {
		log_err(-1, "que_recov", "que_alloc failed");
		return NULL;
	}

	(void)strcpy(pbs_recov_filename, path_queues);
	(void)strcat(pbs_recov_filename, filename);

#ifdef WIN32
	fix_perms(pbs_recov_filename);
#endif

	fds = open(pbs_recov_filename, O_RDONLY, 0);
	if (fds < 0) {
		sprintf(log_buffer, "error opening %s", pbs_recov_filename);
		log_err(errno, "que_recov", log_buffer);
		free((char *)pq);
		return NULL;
	}

#ifdef WIN32
	setmode(fds, O_BINARY);
#endif

	/* read in queue save sub-structure */

	errno = -1;
	if (read(fds, (char *)&pq->qu_qs, sizeof(struct queuefix)) !=
		sizeof(struct queuefix)) {
		sprintf(log_buffer, "error reading %s", pbs_recov_filename);
		log_err(errno, "que_recov", log_buffer);
		free((char *)pq);
		(void)close(fds);
		return NULL;
	}

	/* read in queue attributes */

	if (recov_attr_fs(fds, pq, que_attr_def, pq->qu_attr,
		(int)QA_ATR_LAST, 0) != 0) {
		log_err(-1, "que_recov", "recov_attr[common] failed");
		que_free(pq);
		(void)close(fds);
		return NULL;
	}

	/*
	 * now reload the access control lists, these attributes were
	 * saved separately
	 */

	for (i=0; i < (int)QA_ATR_LAST; i++) {
		if (pq->qu_attr[i].at_type == ATR_TYPE_ACL) {
			recov_acl(&pq->qu_attr[i], &que_attr_def[i],
				que_attr_def[i].at_name, pq->qu_qs.qu_name);
		}
	}

	/* all done recovering the queue */

	(void)close(fds);
	return (pq);
}
