Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 20 Mar 2003 11:11:57 +0200
From:      Ari Suutari <ari.suutari@syncrontech.com>
To:        Brent Verner <brent@rcfile.org>, Doug Poland <doug@polands.org>
Cc:        leafy@leafy.idv.tw, freebsd-java@FreeBSD.ORG
Subject:   Re: Specifying jdk for tomcat 4.1.18
Message-ID:  <200303201111.57618.ari.suutari@syncrontech.com>
In-Reply-To: <20030320041206.GA46244@rcfile.org>
References:  <18118.63.104.35.130.1048108215.squirrel@email.polands.org> <1671.172.16.1.33.1048126904.squirrel@email.polands.org> <20030320041206.GA46244@rcfile.org>

next in thread | previous in thread | raw e-mail | index | archive | help

--Boundary-00=_dXYe+h5We6YEeP6
Content-Type: text/plain;
  charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

Hi,


On Thursday 20 March 2003 06:12, Brent Verner wrote:
> I have a patch to the daemonctl.c source that causes it to use
> JAVA_HOME, but this approach is not ideal, as there are other
> difficulties aside from which java binary to use.


I have also a patched version of daemonctl.c, which
uses a configuration file in /usr/local/etc, like this:
(I'll attach the source to this mail, maybe it is useful
to others also)

tomcat4ctl.conf:

APP_HOME=/usr/local/jakarta-tomcat4.0.5
JAVA_HOME=/usr/local/linux-sun-jdk1.3.1
STDOUT_LOG=/usr/local/jakarta-tomcat4.0.5/logs/stdout.log
STDERR_LOG=/usr/local/jakarta-tomcat4.0.5/logs/stderr.log
STOP_TIMEOUT=5
PID_FILE=/var/run/tomcat4.pid
PORTVERSION=4.0.5
APP_TITLE=Jakarta Tomcat
JAVA_CMD=bin/java
JAR_FILE=bin/bootstrap.jar
JAVA_ARGS=-Djava.protocol.handler.pkgs=com.sun.net.ssl.internal.www.protocol 
-Dc
atalina.home=/usr/local/jakarta-tomcat4.0.5
JAR_ARGS=start


	Ari S.

--Boundary-00=_dXYe+h5We6YEeP6
Content-Type: text/x-csrc;
  charset="iso-8859-1";
  name="daemonctl.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="daemonctl.c"

/*
 * -*- mode: Fundamental; tab-width: 4; -*-
 * ex:ts=4
 *
 * Daemon control program.
 *
 * $FreeBSD: ports/www/jakarta-tomcat4/files/daemonctl.c,v 1.6 2002/05/08 22:00:04 znerd Exp $
 */

#include <assert.h>
#include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>

/* The maximum size of the PID file, in bytes */
#define MAX_FILE_SIZE			32
#define	MAX_ARGS				50

/* The interval in seconds between the checks to make sure the process
   died after a kill */
#define STOP_TIME_INTERVAL		1

#define ERR_ILLEGAL_ARGUMENT				1
#define ERR_PID_FILE_NOT_FOUND				2
#define ERR_PID_FILE_TOO_LARGE				3
#define ERR_PID_FILE_CONTAINS_ILLEGAL_CHAR	4
#define ERR_KILL_FAILED						5
#define ERR_ALREADY_RUNNING					6
#define ERR_NOT_RUNNING						7
#define ERR_CHDIR_TO_APP_HOME				8
#define ERR_ACCESS_JAR_FILE					17
#define ERR_STDOUT_LOGFILE_OPEN				9
#define ERR_STDERR_LOGFILE_OPEN				10
#define ERR_FORK_FAILED						11
#define ERR_STAT_JAVA_HOME					12
#define ERR_JAVA_HOME_NOT_DIR				13
#define ERR_STAT_JAVA_CMD					14
#define ERR_JAVA_CMD_NOT_FILE				15
#define ERR_JAVA_CMD_NOT_EXECUTABLE			16
#define ERR_CONF_FILE_OPEN					17
#define	ERR_CONF_FILE_ERROR					18
#define	ERR_MALLOC							19
#define	ERR_TOO_MANY_ARGS					20
#define	ERR_CONFIG_MISSING					21

#define private static

typedef struct _confNode {

	char*				name;
	char*				value;
	struct _confNode*	next;
} ConfNode;
	

private ConfNode*	confList = NULL;
private char*		myName = "daemonctl";

private void printUsage(void);
private int openPIDFile(void);
private int readPID(int);
private void writePID(int file, int pid);
private void start(void);
private void stop(void);
private void restart(void);
private void readConf(char*);
private char* getConfStr(char*);
private int   getConfInt(char*);


/**
 * Main function. This function is called when this program is executed.
 *
 * @param argc
 *    the number of arguments plus one, so always greater than 0.
 *
 * @param argv
 *    the arguments in an array of character pointers, where the last argument
 *    element is followed by a NULL element.
 */
int main(int argc, char *argv[]) {

	/* Declare variables, like all other good ANSI C programs do :) */
	char *argument;

    readConf(argv[0]);

	/* Parse the arguments */
	if (argc < 2) {
		printUsage();
		return 0;
	}

	setuid(geteuid());
	setgid(getegid());
	argument = argv[1];
	if (strcmp("start", argument) == 0) {
		start();
	} else if (strcmp("stop", argument) == 0) {
		stop();
	} else if (strcmp("restart", argument) == 0) {
		restart();
	} else {
		fprintf(stderr, "%s: Illegal argument \"%s\".\n", myName, argument);
		printUsage();
		exit(ERR_ILLEGAL_ARGUMENT);
	}

	return 0;
}


/**
 * Prints usage information to stdout.
 */
void printUsage(void) {
	printf("Usage: %s [ start | stop | restart ]\n", myName);
}


/**
 * Attempts to open the PID file. If that file is successfully opened, then
 * the file handle (an int) will be returned.
 *
 * @return
 *    the file handle.
 */
int openPIDFile(void) {

 	int file;

	/* Attempt to open the PID file */
	file = open(getConfStr("PID_FILE"), O_RDWR);
	if (file < 0) {
		printf(" [ FAILED ]\n");
		fprintf(stderr, "%s: Unable to open %s for reading and writing: ", myName, getConfStr("PID_FILE"));
		perror(NULL);
		exit(ERR_PID_FILE_NOT_FOUND);
	}

	return file;
}


/**
 * Reads a PID from the specified file. The file is identified by a file
 * handle.
 *
 * @param file
 *    the file handle.
 *
 * @return
 *    the PID, or -1 if the file was empty.
 */
int readPID(int file) {

	char *buffer;
	int hadNewline = 0;
	unsigned int count;
	unsigned int i;
	int pid;

	/* Read the PID file contents */
	buffer = (char *) malloc((MAX_FILE_SIZE + 1) * sizeof(char));
	count = read(file, buffer, MAX_FILE_SIZE + 1);
	if (count > MAX_FILE_SIZE) {
		printf(" [ FAILED ]\n");
		fprintf(stderr, "%s: The file %s contains more than %d bytes.\n", myName, getConfStr("PID_FILE"), MAX_FILE_SIZE);
		exit(ERR_PID_FILE_TOO_LARGE);
	}

	/* Convert the bytes to a number */
	pid = 0;
	for (i=0; i<count; i++) {
		char c = buffer[i];
		if (c >= '0' && c <= '9') {
			char digit = c - '0';
			pid *= 10;
			pid += digit;
		} else if (i == (count - 1) && c == '\n') {
			/* XXX: Ignore a newline at the end of the file */
			hadNewline = 1;
		} else {
			printf(" [ FAILED ]\n");
			fprintf(stderr, "%s: The file %s contains an illegal character (%d) at position %d.\n", myName, getConfStr("PID_FILE"), c, i);
			exit(ERR_PID_FILE_CONTAINS_ILLEGAL_CHAR);
		}
	}
	printf(" [ DONE ]\n");

	if (count == 0 || (count == 1 && hadNewline == 1)) {
		return -1;
	}

	return pid;
}


/**
 * Writes a process ID to the specified file. The file is identified by a file
 * handle.
 *
 * @param file
 *    the file handle, always greater than 0.
 *
 * @param pid
 *    the PID to store, always greater than 0.
 */
void writePID(int file, int pid) {

	char *buffer;
	int nbytes;

	/* Check preconditions */
	assert(file > 0);
	assert(pid > 0);

	printf(">> Writing PID file...");

	lseek(file, (off_t) 0, SEEK_SET);
	ftruncate(file, (off_t) 0);
	nbytes = asprintf(&buffer, "%d\n", pid);
	write(file, buffer, nbytes);
	printf(" [ DONE ]\n");
}


/**
 * Checks if the specified process is running.
 *
 * @param pid
 *    the process id, greater than 0.
 *
 * @return
 *    0 if the specified process is not running, a different value otherwise.
 */
int existsProcess(int pid) {

	int result;

	/* Check preconditions */
	assert(pid > 0);

	/* See if the process exists */
   	result = kill(pid, 0);

	/* If the result is 0, then the process exists */
	if (result == 0) {
		return 1;
	} else {
		return 0;
	}
}


/**
 * Kills the process identified by the specified ID.
 *
 * @param pid
 *    the process id, greater than 0.
 */
void killProcess(int pid) {

	int result;
	unsigned int waited;
	unsigned int forced;
	unsigned int interval = STOP_TIME_INTERVAL;
	unsigned int timeout  = getConfInt("STOP_TIMEOUT");

	/* Check preconditions */
	assert(pid > 0);

	printf(">> Terminating process %d...", pid);
	result = kill(pid, SIGTERM);
	if (result < 0) {
		printf(" [ FAILED ]\n");
		fprintf(stderr, "%s: Unable to kill process %d: ", myName, pid);
		perror(NULL);
		exit(ERR_KILL_FAILED);
	}

	/* Wait until the process is actually killed */
    result = existsProcess(pid);
	for (waited=0; result == 1 && waited < timeout; waited += interval)
	{
		printf(".");
		fflush(NULL);
		sleep(interval);
    	result = existsProcess(pid);
	}

	/* If the process still exists, then have no mercy and kill it */
	forced = 0;
	if (result == 1) {

		/* Force the process to die */
		result = kill(pid, SIGKILL);
		if (result == 0) {
			forced = 1;
			printf(" [ DONE ]\n");
			fprintf(stderr, "%s: Process %d did not terminate within %d sec. Killed.\n", myName, timeout, pid);
		} else if (result != ESRCH) {
			printf(" [ FAILED ]\n");
			fprintf(stderr, "%s: Unable to kill process %d: ", myName, pid);
			perror(NULL);
			exit(ERR_KILL_FAILED);
		}
	}

	if (forced == 0) {
		printf(" [ DONE ]\n");
	}
}


/**
 * Starts the daemon.
 */
void start(void) {

	int file;
	int pid;
	int result;
	int stdoutLogFile;
	int stderrLogFile;
	struct stat sb;
	char	buf[512];
	char*	token;
	char*	args[MAX_ARGS];
	int		argCount;

	/* Open and read the PID file */
	printf(">> Reading PID file (%s)...", getConfStr("PID_FILE"));
	file = openPIDFile();
	pid = readPID(file);

	printf(">> Starting %s %s...", getConfStr("APP_TITLE"), getConfStr("PORTVERSION"));
	if (pid != -1) {

		/* Check if the process actually exists */
		result = existsProcess(pid);
		if (result == 1) {
			printf(" [ FAILED ]\n");
			fprintf(stderr, "%s: %s %s is already running, PID is %d.\n", myName, getConfStr("APP_TITLE"), getConfStr("PORTVERSION"), pid);
			exit(ERR_ALREADY_RUNNING);
		}
	}

	/* Check if the JDK home directory is actually a directory */
	result = stat(getConfStr("JAVA_HOME"), &sb);
	if (result != 0) {
		printf(" [ FAILED ]\n");
		fprintf(stderr, "%s: Unable to stat %s: ", myName, getConfStr("JAVA_HOME"));
		perror(NULL);
		exit(ERR_STAT_JAVA_HOME);
	}
	if (!S_ISDIR(sb.st_mode)) {
		printf(" [ FAILED ]\n");
		fprintf(stderr, "%s: Java home directory %s is not a directory.\n", myName, getConfStr("JAVA_HOME"));
		exit(ERR_JAVA_HOME_NOT_DIR);
	}

	/* Check if the Java command is actually an executable regular file */
    sprintf (buf, "%s/%s", getConfStr("JAVA_HOME"), getConfStr("JAVA_CMD"));
	result = stat(buf, &sb);
	if (result != 0) {
		printf(" [ FAILED ]\n");
		fprintf(stderr, "%s: Unable to stat %s: ", myName, buf);
		perror(NULL);
		exit(ERR_STAT_JAVA_CMD);
	}
	if (!S_ISREG(sb.st_mode)) {
		printf(" [ FAILED ]\n");
		fprintf(stderr, "%s: Java command %s is not a regular file.\n", myName, buf);
		exit(ERR_JAVA_CMD_NOT_FILE);
	}
	result = access(buf, X_OK);
	if (result != 0) {
		printf(" [ FAILED ]\n");
		fprintf(stderr, "%s: Java command %s is not executable: ", myName, buf);
		perror(NULL);
		exit(ERR_JAVA_CMD_NOT_EXECUTABLE);
	}

	/* Change directory */
	result = chdir(getConfStr("APP_HOME"));
	if (result < 0) {
		printf(" [ FAILED ]\n");
		fprintf(stderr, "%s: Unable to access directory %s: ", myName, getConfStr("APP_HOME"));
		perror(NULL);
		exit(ERR_CHDIR_TO_APP_HOME);
	}

	/* See if the JAR file exists */
	sprintf(buf, "%s/%s", getConfStr("APP_HOME"), getConfStr("JAR_FILE"));
	result = access(buf, R_OK);
	if (result < 0) {
		printf(" [ FAILED ]\n");
		fprintf(stderr, "%s: Unable to access JAR file %s: ", myName, buf);
		perror(NULL);
		exit(ERR_ACCESS_JAR_FILE);
	}

	/* Open the stdout log file */
	stdoutLogFile = open(getConfStr("STDOUT_LOG"), O_WRONLY);
	if (stdoutLogFile < 0) {
		printf(" [ FAILED ]\n");
		fprintf(stderr, "%s: Unable to open %s for writing: ", myName, getConfStr("STDOUT_LOG"));
		perror(NULL);
		exit(ERR_STDOUT_LOGFILE_OPEN);
	}
	lseek(stdoutLogFile, (off_t) 0, SEEK_END);

	/* Open the stderr log file */
	stderrLogFile = open(getConfStr("STDERR_LOG"), O_WRONLY);
	if (stderrLogFile < 0) {
		printf(" [ FAILED ]\n");
		fprintf(stderr, "%s: Unable to open %s for writing: ", myName, getConfStr("STDERR_LOG"));
		perror(NULL);
		exit(ERR_STDERR_LOGFILE_OPEN);
	}
	lseek(stderrLogFile, (off_t) 0, SEEK_END);

	argCount = 0;
	sprintf(buf, "%s/%s", getConfStr("JAVA_HOME"), getConfStr("JAVA_CMD"));
	args[argCount++] = strdup(buf);
    token = strtok(getConfStr("JAVA_ARGS"), " ");
    while (token != NULL) {
         
		if (argCount == MAX_ARGS - 1) {

			printf(" [ FAILED ]\n");
			fprintf (stderr, "Too many args\n");
			exit (ERR_TOO_MANY_ARGS);
		}

		args[argCount++] = strdup (token);
		token = strtok(NULL, " ");
	}

	if (argCount >= MAX_ARGS - 2) {

		printf(" [ FAILED ]\n");
		fprintf (stderr, "Too many args\n");
		exit (ERR_TOO_MANY_ARGS);
	}

	args[argCount++] = "-jar";
	args[argCount++] = getConfStr("JAR_FILE");

    token = strtok(getConfStr("JAR_ARGS"), " ");
    while (token != NULL) {
         
		if (argCount == MAX_ARGS - 1) {

		    printf(" [ FAILED ]\n");
			fprintf (stderr, "Too many args\n");
			exit (ERR_TOO_MANY_ARGS);
		}

		args[argCount++] = strdup (token);
		token = strtok(NULL, " ");
	}

	args[argCount++] = NULL;

	/* Split this process in two */
	pid = fork();
	if (pid == -1) {
		printf(" [ FAILED ]\n");
		fprintf(stderr, "%s: Unable to fork: ");
		perror(NULL);
		exit(ERR_FORK_FAILED);
	}

	if (pid == 0) {

		/* Redirect stdout to log file */
		dup2(stdoutLogFile, STDOUT_FILENO);

		/* Redirect stderr to log file */
		dup2(stderrLogFile, STDERR_FILENO);

		/* TODO: Support redirection of both stdout and stderr to the same
		         file using pipe(2) */

		/* Execute the command */
		execv(args[0], args);

		fprintf(stderr, "%s: Unable to start %s %s since '%s/%s -jar %s' in %s: ", myName, getConfStr("APP_TITLE"), getConfStr("PORTVERSION"), getConfStr("JAVA_HOME"), getConfStr("JAVA_CMD"), getConfStr("JAR_FILE"), getConfStr("APP_HOME"));
		perror(NULL);
	} else {
		printf(" [ DONE ]\n");
		writePID(file, pid);
	}
}

/**
 * Stops the daemon.
 */
void stop(void) {

	int file;
	int pid;

	/* Open and read the PID file */
	printf(">> Reading PID file (%s)...", getConfStr("PID_FILE"));
	file = openPIDFile();
	pid = readPID(file);

	printf(">> Checking if %s %s is running...", getConfStr("APP_TITLE"), getConfStr("PORTVERSION"));

	/* If there is a PID, see if the process still exists */
	if (pid != -1) {
		int result = kill(pid, 0);
		if (result != 0 && errno == ESRCH) {
			ftruncate(file, (off_t) 0);
			pid = -1;
		}
	}

	/* If there is no running process, produce an error */
	if (pid == -1) {
		printf(" [ FAILED ]\n");
		fprintf(stderr, "%s: %s %s is currently not running.\n", myName, getConfStr("APP_TITLE"), getConfStr("PORTVERSION"));
		exit(ERR_NOT_RUNNING);
	}
	printf(" [ DONE ]\n");

	/* Terminate the process */
	killProcess(pid);

	/* Clear the PID file */
	ftruncate(file, (off_t) 0);
}


/**
 * Restarts the process. If it not currently running, then it will fail.
 */
void restart(void) {
	stop();
	start();
}


void readConf(char* prog)
{
    FILE*		confFile;
	char*		ptr;
	char		confName[80];
	char		buf[256];
	ConfNode*	node;

    ptr = strrchr(prog, '/');
    if (ptr == NULL)
        ptr = prog;
    else
         ptr++;

	myName = strdup(ptr);
	sprintf (confName, "/usr/local/etc/%s.conf", ptr);
    printf ("Reading conf file %s...\n", confName);

    confFile = fopen(confName, "r");
    if (confFile == NULL) {

		fprintf (stderr, "Cannot open configuration file %s ", confName);
		perror(NULL);
		exit (ERR_CONF_FILE_OPEN);
	}

	while (fgets (buf, sizeof(buf), confFile) != NULL) {

		if (buf[0] == '#')
			continue;

		ptr = strchr(buf, '\n');
		if (ptr == NULL) {

			fprintf (stderr, "Too long line: %s\n", buf);
			exit(ERR_CONF_FILE_ERROR);
		}

		*ptr = '\0';
		ptr = strchr(buf, '=');
		if (ptr == NULL) {

			fprintf (stderr, "No equal sign found: %s\n", buf);
			exit (ERR_CONF_FILE_ERROR);
		}

		node = malloc(sizeof(ConfNode));
		if (node == NULL) {

			fprintf (stderr, "Out of memory.\n");
			exit (ERR_MALLOC);
		}

		*ptr = '\0';
		node->name  = strdup (buf);
		node->value = strdup(ptr + 1);
		node->next  = confList;
		confList    = node;
	}

	fclose (confFile);
}

char* getConfStr(char* name)
{
	ConfNode*	node;

	node = confList;
	while (node != NULL) {

		if (!strcmp(node->name, name))
			return node->value;

		node = node->next;
	}

	fprintf (stderr, "Configuration value missing for %s\n", name);
	exit (ERR_CONFIG_MISSING);
	return NULL;
}

int getConfInt(char* name)
{
	return atoi(getConfStr(name));
	
}

--Boundary-00=_dXYe+h5We6YEeP6--


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-java" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200303201111.57618.ari.suutari>