You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@uima.apache.org by cw...@apache.org on 2013/01/02 20:47:17 UTC
svn commit: r1427975 - in /uima/sandbox/uima-ducc/trunk/uima-ducc-spawn/src:
Makefile ducc_ling.c
Author: cwiklik
Date: Wed Jan 2 19:47:16 2013
New Revision: 1427975
URL: http://svn.apache.org/viewvc?rev=1427975&view=rev
Log:
UIMA-2491
Added:
uima/sandbox/uima-ducc/trunk/uima-ducc-spawn/src/Makefile (with props)
uima/sandbox/uima-ducc/trunk/uima-ducc-spawn/src/ducc_ling.c (with props)
Added: uima/sandbox/uima-ducc/trunk/uima-ducc-spawn/src/Makefile
URL: http://svn.apache.org/viewvc/uima/sandbox/uima-ducc/trunk/uima-ducc-spawn/src/Makefile?rev=1427975&view=auto
==============================================================================
--- uima/sandbox/uima-ducc/trunk/uima-ducc-spawn/src/Makefile (added)
+++ uima/sandbox/uima-ducc/trunk/uima-ducc-spawn/src/Makefile Wed Jan 2 19:47:16 2013
@@ -0,0 +1,7 @@
+CFLAGS = -g
+
+
+all: ducc_ling
+
+clean:
+ rm ducc_ling
Propchange: uima/sandbox/uima-ducc/trunk/uima-ducc-spawn/src/Makefile
------------------------------------------------------------------------------
svn:eol-style = native
Added: uima/sandbox/uima-ducc/trunk/uima-ducc-spawn/src/ducc_ling.c
URL: http://svn.apache.org/viewvc/uima/sandbox/uima-ducc/trunk/uima-ducc-spawn/src/ducc_ling.c?rev=1427975&view=auto
==============================================================================
--- uima/sandbox/uima-ducc/trunk/uima-ducc-spawn/src/ducc_ling.c (added)
+++ uima/sandbox/uima-ducc/trunk/uima-ducc-spawn/src/ducc_ling.c Wed Jan 2 19:47:16 2013
@@ -0,0 +1,540 @@
+//-------------------------------------------------------------------------------
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//-------------------------------------------------------------------------------
+// --------------------------------------------------------------------------------
+// IMPORTANT IMPORTANT IMPORTANT
+// ALWAYS update the version even for trivial changes
+// IMPORTANT IMPORTANT IMPORTANT
+// --------------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+
+#define VERSION "0.6.4"
+
+/**
+ * 2012-05-04 Support -w <workingdir>. jrc.
+ * 2012-05-04 0.6.0 Update version to match current DUCC beta. jrc.
+ * 2012-05-13 0.6.1 Update for MAC getpwnam() bug jrc.
+ * 2012-05-13 0.6.2 Update to change group as well as userid. jrc.
+ * 2012-07-12 0.6.3 RLIMIT_CORE support. jrc
+ * 2012-10-04 0.6.4 renice. jrc
+ */
+
+/**
+ * BUFFLEN is largest size for our stack buffers.
+ * STRLEN is longest string we'll place into a stack buffer.
+ * MAX_COMPONENTS Is the largest number of intermediate components on a path passed by -b flag,
+ * This is controlled by DUCC itself so it's not subject to the user's whims.
+ */
+#define BUFLEN (PATH_MAX)
+#define STRLEN (BUFLEN-1)
+#define MAX_COMPONENTS (10)
+
+void version()
+{
+ fprintf(stdout, "050 ducc_ling Version %s compiled %s at %s\n", VERSION, __DATE__, __TIME__);
+}
+
+
+void usage()
+{
+ fprintf(stderr, "999 Usage:\n");
+ fprintf(stderr, "999 ducc_ling <-u user> [-w workingdir] [-f filepath] -- program_name [program args]\n");
+ exit(1);
+}
+
+/**
+ * Make a subdirectory.
+ *
+ * Number 2000
+ */
+int mksubdir(char *path)
+{
+ char buf[BUFLEN];
+ struct stat statbuf;
+
+ // if it exists and is a dir just return
+ if ( stat(path, &statbuf) == 0 ) {
+ fprintf(stdout, "2210 Directory %s already exists.\n", path);
+ if ( ! ( statbuf.st_mode & S_IFDIR) ) {
+ fprintf(stderr, "2200 Log base %s is not a directory\n", path);
+ return 0;
+ }
+ return 1;
+ }
+
+ fprintf(stdout, "2000 Creating directory %s\n", path);
+ if ( mkdir(path, 0750) != 0 ) {
+
+ if ( errno == EEXIST ) {
+ // Terribly, terribly ugly. Parts of the directory might be made already in the
+ // CLI. It is observed that if NFS is slow, or system dates are a bit off, when this
+ // this code starts to run, the existance check above may fail, but the attempt to
+ // make the directory will now fail with "already exists". So we simply repeat the
+ // stat to make sure it's a directory and not a regular file.
+ if ( stat(path, &statbuf) == 0 ) {
+ fprintf(stdout, "2210 Directory %s already exists.\n", path);
+ if ( ! ( statbuf.st_mode & S_IFDIR) ) {
+ fprintf(stderr, "2200 Log base %s is not a directory\n", path);
+ return 0;
+ }
+ return 1;
+ }
+ }
+
+ snprintf(buf, STRLEN, "2100 Cannot create log path component %s", path);
+ buf[STRLEN] = '\0';
+ perror(buf);
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * Concatenate thing to buf inplace within buf without overstepping BUFFLEN.
+ * We assume buf is a buffer of length BUFLEN and will forcibly termintate the
+ * the string at the end of the buffer.
+ *
+ * Number 3000
+ */
+void concat(char *buf, const char *thing)
+{
+ //
+ // Shouldn't happen unless we're sloppy or being hacked. Die hard and fast.
+ //
+ if ( (strlen(buf) + strlen(thing) + 1 ) > BUFLEN ) {
+ fprintf(stderr, "3000 Buffer overflow: string length too long to concatenate %s and %s. maxlen = %d\n",
+ buf, thing, BUFLEN);
+ exit(1);
+ }
+ strncat(buf, thing, STRLEN);
+ buf[STRLEN] = '\0';
+}
+
+
+/**
+ * Walk the directory structure. Check access until we find a spot where the dir does
+ * not exist. At this point the dir must have rwx privs and all components be dirs. If not,
+ * error exit. If so, then begin creating directories to create the full path.
+ *
+ * The path created is base/subdir/jobid.
+ *
+ * base is the user-specified log location.
+ * filestem is the agent-specified log name, minus the required pid
+ */
+char * mklogfile(const char *filepath)
+{
+ //
+ // First step, the base must exist and be writable.
+ //
+ char buf[BUFLEN];
+ char *path_components[MAX_COMPONENTS];
+ char *next_tok = NULL;
+ char *final_tok = NULL;
+
+ int i,j = 0;
+ char *tmp;
+ char *fullpath = strdup(filepath);
+
+ i = 0;
+ printf("Dir: %s\n", fullpath);
+ for ( next_tok = strtok(fullpath, "/"); next_tok; next_tok = strtok(NULL, "/") ) {
+ printf("Component %d: %s\n", i, next_tok);
+ path_components[i++] = next_tok;
+ }
+
+ buf[0] = '\0'; // make it into a "" string
+ if ( filepath[0] == '/' ) { // strtok removes the '/'es so if it's absolute path, need to put one back in
+ concat(buf, "/");
+ }
+ for ( j = 0; j < i-1; j++ ) {
+ concat(buf, path_components[j]);
+ if ( ! mksubdir(buf) ) {
+ return NULL;
+ }
+ concat(buf, "/");
+ }
+
+ tmp = strdup(buf);
+ snprintf(buf, STRLEN, "%s%s-%d.log", tmp, path_components[i-1], getpid());
+ return strdup(buf);
+}
+
+char * mklogfileOld(const char *base, char *subdir)
+{
+ //
+ // First step, the base must exist and be writable.
+ //
+ char buf[BUFLEN];
+ char *path_components[MAX_COMPONENTS];
+ char *next_tok = NULL;
+ char *final_tok = NULL;
+ char * jobid = getenv("JobId");
+
+ if ( jobid == NULL ) {
+ fprintf(stderr, "2230 Environment must contain \"JobId\" in order to write to a log file.\n");
+ return;
+ }
+
+ int i,j = 0;
+
+ char *tmp;
+
+ struct stat statbuf;
+
+ // if it exists and is a dir just return
+ if ( stat(base, &statbuf) == 0 ) {
+ fprintf(stdout, "2210 Directory %s exists.\n", base);
+ if ( ! ( statbuf.st_mode & S_IFDIR) ) {
+ fprintf(stderr, "2200 Log base %s is not a directory\n", base);
+ return NULL;
+ }
+ }
+
+ if ( access(base, R_OK + W_OK + X_OK) != 0 ) { // make sure I can use the base dir
+ snprintf(buf, STRLEN, "Can't access %s: %d\n", base, errno);
+ buf[STRLEN] = '\0';
+ perror(buf);
+ return NULL;
+ }
+
+ i = 0;
+ for ( next_tok = strtok(subdir, "/"); next_tok; next_tok = strtok(NULL, "/") ) {
+ path_components[i++] = next_tok;
+ }
+
+ buf[0] = '\0'; // make it into a "" string
+ concat(buf, base);
+ for ( j = 0; j < i-1; j++ ) {
+ concat(buf, "/");
+ concat(buf, path_components[j]);
+
+ if ( ! mksubdir(buf) ) {
+ return NULL;
+ }
+ }
+
+ concat(buf, "/");
+ concat(buf, jobid);
+ if ( ! mksubdir(buf) ) {
+ fprintf(stderr, "Can't make directory %s, quitting.\n", buf);
+ return NULL;
+ }
+
+ tmp = strdup(buf);
+ snprintf(buf, STRLEN, "%s/%s-%d.log", tmp, path_components[i-1], getpid());
+ return strdup(buf);
+}
+
+void show_env(char **envp)
+{
+ int count = -1;
+ while ( envp[++count] != NULL ) {
+ fprintf(stdout, "Envoron[%d] = %s\n", count, envp[count]);
+ }
+}
+
+/**
+ * If DUCC_RLIMIT_CORE is in env, get its value and set the soft core limit.
+ *
+ * Number 4000
+ */
+void set_limits()
+{
+ char *climit = getenv("DUCC_RLIMIT_CORE");
+ char buf[BUFLEN];
+
+ if ( climit != NULL ) {
+ char *en = 0;
+ long long lim = strtoll(climit, &en, 10);
+ struct rlimit limstruct;
+
+ fprintf(stdout, "4000 Setting RLIMIT_CORE\n");
+ if (*en) {
+ fprintf(stderr, "4010 DUCC_RLIMIT_CORE is not numeric; core limit note set: %s\n", climit);
+ return;
+ }
+
+ getrlimit(RLIMIT_CORE, &limstruct);
+ fprintf(stdout, "4030 Before: RLIMIT_CORE soft[%lld] hard[%lld]\n", limstruct.rlim_cur, limstruct.rlim_max);
+
+ limstruct.rlim_cur = lim;
+ int rc = setrlimit(RLIMIT_CORE, &limstruct);
+ if ( rc != 0 ) {
+ perror("4030 Core soft limit was not set.");
+ }
+
+ getrlimit(RLIMIT_CORE, &limstruct);
+ fprintf(stdout, "4040 After: RLIMIT_CORE soft[%lld] hard[%lld]\n", limstruct.rlim_cur, limstruct.rlim_max);
+ }
+}
+
+#ifndef __APPLE__
+void renice()
+{
+ char *nicestr = getenv("DUCC_NICE");
+ int niceval = 10;
+ if ( nicestr != NULL ) {
+ char *en = 0;
+ niceval = strtol(nicestr, &en, 10);
+ }
+ fprintf(stdout, "4050 Nice: Using %d\n", niceval);
+ int rc = nice(niceval);
+ if ( rc < 0 ) {
+ perror("4060 Can't set nice.");
+ }
+}
+#else
+void renice()
+{
+ // mac seems to have no 'nice' syscall but we don't care since its only for test and devel anyway
+}
+#endif
+
+/**
+ * Proposed calling conventtion:
+ * ducc_ling <duccling args> -- executable_name <executable args>
+ * Where
+ * executable is whatever, usually the path to the jova binary
+ * <executable args> are whatever you want to start java with, probably
+ * the JVM parms followed by the app parms
+ * <duccling args> are args for ducc_ling to process. Perhaps something like:
+ * -u <userid> - userid to switch to
+ * -f <filepath> - if provided, ducc_ling will attempt to use this as
+ * the log path. The string <pid>.log is appended, where
+ * <pid> is the process id. Intermediate directories are
+ * created as needed.
+ * -w <workingdir> - if provided, ducc_ling will attempt to cd to the
+ * specified dir as workingdir before execing to
+ * the indicated process.
+ *
+ * If -f is missing, no redirection is performed and no files are created.
+ */
+int main(int argc, char **argv, char **envp)
+{
+ int i;
+ int opt;
+ char *userid = NULL;
+ char *filepath = NULL;
+ char *workingdir = NULL;
+ struct passwd *pwd= NULL;
+ int switch_ids = 0;
+ int redirect = 1;
+ char buf[BUFLEN];
+
+ version(); // this gets echoed into the Agent's log
+
+ // dont allow root to exec a process
+ if ( getuid() == 0 ) {
+ fprintf(stderr, "400 Can't run ducc_ling as root\n");
+ exit(1);
+ }
+
+ while ( (opt = getopt(argc, argv, "f:w:u:h?") ) != -1) {
+ switch (opt) {
+ case 'u':
+ userid = optarg;
+ break;
+ case 'f':
+ filepath = optarg;
+ break;
+ case 'w':
+ workingdir = optarg;
+ break;
+ case 'h':
+ case '?':
+ usage();
+ default:
+ fprintf(stderr, "100 Unrecognized argument %s\n", optarg);
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if ( userid == NULL ) {
+ fprintf(stderr, "200 missing userid\n");
+ exit(1);
+ }
+
+ if ( filepath == NULL ) {
+ fprintf(stdout, "300 Bypassing redirect of log\n");
+ redirect = 0;
+ }
+
+ // do this here before redirection stdout / stderr
+ fprintf(stdout, "0 %d\n", getpid()); // code 0 means we passed tests and are about to dup I/O
+
+ // fetch "ducc" user passwd structure
+ pwd = getpwnam("ducc");
+
+ if ( pwd == NULL ) {
+ pwd = getpwuid(getuid());
+#ifdef __APPLE__
+ // Seems theres a bug in getpwuid and nobody seems to have a good answer. On mac we don't
+ // care anyway so we ignore it.
+ if ( pwd == NULL ) {
+ fprintf(stdout, "600 No \"ducc\" user found and I can't find my own name. Running as id %d", getuid());
+ } else {
+ fprintf(stdout, "600 No \"ducc\" user found, running instead as %s.\n", pwd->pw_name);
+ }
+#else
+ fprintf(stdout, "600 No \"ducc\" user found, running instead as %s.\n", pwd->pw_name);
+#endif
+ } else if ( pwd->pw_uid != getuid() ) {
+ fprintf(stdout, "700 Caller is not ducc (%d), not switching ids ... \n", pwd->pw_uid);
+ pwd = getpwuid(getuid());
+ fprintf(stdout, "800 Running instead as %s.\n", pwd->pw_name);
+ //exit(0);
+ } else {
+ switch_ids = 1;
+ }
+
+ // fetch given user's passwd structure and try switch identities.
+ if ( switch_ids ) {
+ pwd = getpwnam(userid);
+ if ( pwd == NULL ) {
+ fprintf(stderr, "820 User \"%s\" does not exist.\n", userid);
+ exit(1);
+ }
+
+ // dont allow to change uid to root.
+ if ( pwd->pw_uid == 0 ) {
+ fprintf(stderr, "900 setuid to root not allowed. Exiting.\n");
+ exit(1);
+ }
+
+ if ( setgid(pwd->pw_gid) != 0 ) {
+ snprintf(buf, STRLEN, "1100 Unable to switch to group id %d.",pwd->pw_gid);
+ buf[STRLEN] = '\0';
+ perror(buf);
+ } else {
+ fprintf(stdout, "830 Switched to group %d.\n", pwd-> pw_gid);
+ }
+
+ if ( setuid(pwd->pw_uid) != 0 ) {
+ snprintf(buf, STRLEN, "1100 Unable to switch to user id %s.",userid);
+ buf[STRLEN] = '\0';
+ perror(buf);
+ } else {
+ fprintf(stdout, "840 Switched to user %d.\n", pwd-> pw_uid);
+ }
+ }
+
+ set_limits(); // AFTER the switch, set soft limits if needed
+ renice();
+
+ if ( workingdir != NULL ) {
+ int rc = chdir(workingdir);
+ if ( rc == -1 ) {
+ snprintf(buf, STRLEN, "1110 Unable to switch to working director %s.", workingdir);
+ buf[STRLEN] = '\0';
+ perror(buf);
+ exit(1);
+ }
+ fprintf(stdout, "1120 Changed to working dir %s\n", workingdir);
+ }
+
+ //
+ // Set up logging dir. We have swithed by this time so we can't do anything the user couldn't do.
+ //
+ if ( redirect ) {
+ char *logfile = mklogfile(filepath);
+ if ( logfile == NULL ) exit(1); // mklogdir creates sufficient erro rmessages
+
+ //snprintf(buf, STRLEN, "%s/%s-%d.log", logdir, log, getpid());
+ //buf[STRLEN] = '\0';
+
+ fprintf(stdout, "1200 Redirecting stdout and stderr to %s as uid %d euid %d\n", logfile, getuid(), geteuid());
+
+ fflush(stdout);
+ fflush(stderr);
+
+ // do we want apend or trunc?
+ int fd = open(logfile, O_CREAT + O_WRONLY + O_TRUNC, 0644);
+ // dup stdout and stderr into the log file
+ if ( fd >= 0 ) {
+ int rc1 = dup2(fd, 1);
+ int rc2 = dup2(fd, 2);
+ } else {
+ snprintf(buf, STRLEN, "1300 cannot open file %s", logfile);
+ buf[STRLEN] = '\0';
+ perror(buf);
+ exit(1);
+ }
+
+ version(); // this gets echoed into the redirected log
+ }
+
+ //
+ // Translate DUCC_LD_LIBRARY_PATH into LD_LIBRARY_PATH, if it exists.
+ //
+ //show_env(envp);
+
+ int env_index = -1;
+ char ** pathstr = NULL;
+ while ( envp[++env_index] != NULL ) {
+ char *srchstring = "DUCC_LD_LIBRARY_PATH=";
+ int len = strlen(srchstring);
+ if ( strncmp(envp[env_index], srchstring, len) == 0 ) {
+ fprintf(stdout, "3000 Found DUCC_LD_LIBRARY_PATH and it is %s\n", envp[env_index]);
+ pathstr = &envp[env_index];
+ break;
+ }
+ }
+ if ( pathstr == NULL ) {
+ fprintf(stdout, "3001 Did not find DUCC_LD_LIBRARY_PATH, not setting LD_LIBRARY_PATH.\n");
+ } else {
+ //
+ // We modify the variable in place.
+ //
+ char *val = getenv("DUCC_LD_LIBRARY_PATH");
+ fprintf(stdout, "3002 Changing DUCC_LD_LIBRARY_PATH to LD_LIBRARY_PATH\n");
+ sprintf(*pathstr, "LD_LIBRARY_PATH=%s", val);
+ }
+
+
+ //
+ // Now just transmogrify into the requested command
+ //
+ fprintf(stdout, "1000 Command to exec: %s\n", argv[0]);
+ for ( i = 1; i < argc; i++ ) {
+ fprintf(stdout, " arg[%d]: %s\n", i, argv[i]);
+ }
+ execve(argv[0], argv, envp); // just run the passed-in command
+
+ //
+ // if we get here it's because exec failed - it never returns if it succeeds.
+ //
+ snprintf(buf, STRLEN, "1400 cannot exec %s", argv[0]);
+ buf[STRLEN] = '\0';
+ perror(buf);
+ exit(1);
+}
Propchange: uima/sandbox/uima-ducc/trunk/uima-ducc-spawn/src/ducc_ling.c
------------------------------------------------------------------------------
svn:eol-style = native