head	1.3;
access;
symbols
	caudium_1_3_21:1.1
	caudium_1_3_20:1.1
	caudium_1_3_19:1.1
	caudium_1_3_18:1.1
	caudium_1_3_17:1.1
	caudium_1_3_16:1.1
	caudium_1_3_15:1.1
	caudium_1_3_14:1.1;
locks; strict;
comment	@# @;


1.3
date	2003.10.21.19.01.19;	author hww3;	state dead;
branches;
next	1.2;

1.2
date	2003.10.16.22.46.55;	author hww3;	state Exp;
branches;
next	1.1;

1.1
date	2003.02.10.17.27.31;	author grendel;	state Exp;
branches;
next	;


desc
@@


1.3
log
@its no longer a work in progress
@
text
@#!@@PIKEBIN@@
/*
 * Caudium - An extensible World Wide Web server
 * Copyright  2003-2000 The Caudium Group
 * 
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */
 
/*
 * Authors: 
 *   Marek Habersack <grendel@@caudium.net>
 *   Bill Welliver <hww3@@riverweb.com>
 *
 * License: MPL/LGPL
 * 
 * $Id: start,v 1.2 2003/10/16 22:46:55 hww3 Exp $
 */
 
// for convenience
string pikever = sprintf("%u.%u.%u/", __REAL_MAJOR__, __REAL_MINOR__, __REAL_BUILD__);

// arguments we understand
array(array(string | array(string))) arguments = ({
  ({"quiet", Getopt.NO_ARG, ({"--quiet"})}),
  ({"truss", Getopt.NO_ARG, ({"--truss"})}),
  ({"strace", Getopt.NO_ARG, ({"--strace"})}),
  ({"ltrace", Getopt.NO_ARG, ({"--ltrace"})}),
  ({"log-dir", Getopt.HAS_ARG, ({"--log-dir", "--logdir"})}),
  ({"config-dir", Getopt.HAS_ARG, ({"--config-dir", "--configdir"})}),
  ({"pike-version", Getopt.HAS_ARG, ({"--pike-version", "--pikeversion"})}),
  ({"pid-file", Getopt.HAS_ARG, ({"--pid-file", "--pidfile"})}),
  ({"debug", Getopt.NO_ARG, ({"--debug", "--with-debug", "--enable-debug"})}),
  ({"nodebug", Getopt.NO_ARG, ({"--witout-debug", "--nodebug", "--disable-debug"})}),
  ({"fddebug", Getopt.NO_ARG, ({"--fd-debug", "--with-fd-debug", "--enable-fd-debug"})}),
  ({"threads", Getopt.NO_ARG, ({"--threads", "--with-threads", "--enable-threads"})}),
  ({"nothreads", Getopt.NO_ARG, ({"--no-threads", "--without-threads", "--disable-threads"})}),
  ({"profile", Getopt.NO_ARG, ({"--profile", "--with-profile", "--enable-profile"})}),
  ({"fileprofile", Getopt.NO_ARG, ({"--file-profile", "--with-file-profile", "--enable-file-profile"})}),
  ({"keepalive", Getopt.NO_ARG, ({"--keep-alive", "--with-keep-alive", "--enable-keep-alive"})}),
  ({"pike", Getopt.HAS_ARG, ({"--with-pike"})}),
  ({"once", Getopt.NO_ARG, ({"--once"})}),
  ({"gdb", Getopt.MAY_HAVE_ARG, ({"--gdb"}), 0, "gdb"}),
  ({"program", Getopt.HAS_ARG, ({"--program"})}),
  ({"version", Getopt.NO_ARG, ({"--version"})}),
  ({"help", Getopt.NO_ARG, ({"--help", "-?"})})
});

// loader options (with defaults)
mapping(string:mixed) options = ([
  "gdb" : "gdb",
  "pike" : 0,
  "pikever" : 0,
  "threads" : 1,
  "program" : "base_server/caudiumloader.pike", 
]);

// default directory locations. Relative to the caudium server dir.
mapping(string:string|array) dirs = ([
  "LOG" : "../logs/",
  "CONFIG" : "../configurations/",
  "extra_modpath" : ({
    "etc/modules/"
  }),
  "extra_incpath" : ({
    "etc/include",  "base_server"
  }),
  "extra_progpath" : ({
  })
]);

// environment variables we set for caudium
// all of them are merged with the values of variables from the shell
// environment, if they exist. The existing variables are appended to the
// values below. Some of the variables are constructed dynamically, those
// are not included in the mapping below.
mapping(string:string|array) envvars = ([
  "CLASSPATH" : "etc/classes:etc/classes/roxen_servlet.jar:etc/classes/jsdk.jar",
  "PIKE_MODULE_PATH" : getenv("PIKE_MODULE_PATH") || "",
]);

// arguments that should go to pike
array pike_args=({});

// parsed arguments
array parsed_args=({});

// components of the Caudium command line.
mapping(string:string|array) command_line = ([
  "DEFINES" : ({}),
  "INCLUDES" : ({}),
  "COTHER": ({})
]);

// a list of valid interpreters we should try to use.
array valid_interpreters=({"bin/caudium", "bin/pike"});

// standard descriptors for caudium
Stdio.File   stdout;
Stdio.File   stderr;

// load the environment variables

int daemonize()
{
  if (fork())
    return 0;

  Stdio.stdin->close();
  Stdio.stdout->close();
  Stdio.stderr->close();

  System.setsid();

  return 0;
}

// run Caudium and return its exit status.
int run_caudium(array(string) args, mapping|void opts)
{
  Process.Process   proc;
  mapping           myopts = opts || ([]);

  opts->cwd = getcwd();
  opts->env = getenv() | envvars;
  write("args: %O opts: %O", args, opts);
  proc = Process.create_process(args, opts);

  if (!proc) {
    stderr->write("Failed to execute the child process\n");
    return 1;
  }

  return proc->wait();
}

void append_env_path(string envvar, string value)
{
  if (!envvar || !sizeof(envvar))
    return;
  
  if (envvars[envvar] && sizeof(envvars[envvar]))
    envvars[envvar] += ":";
  envvars[envvar] += value;
}

// Sets some initial and extra values. This function MUST be ran with the
// cwd set to the caudium toplevel directory.
void preamble()
{
  mapping(string:string)  osdata = System.uname();
  int                     go_threads = 0;
  string                  os_label = "";
  array(string)           os_rel;
  Stdio.Stat              fstat;

  if (options->pikever) {
    fstat = file_stat("bin/caudium-" + options->pikever);
    if (fstat && fstat->isreg && (fstat->mode & 0111))
      options->pike = "bin/caudium-" + options->pikever;
    if (!options->pike) {
      fstat = file_stat("bin/pike-" + options->pikever);
      if (fstat && fstat->isreg && (fstat->mode & 0111))
        options->pike = "bin/pike-" + options->pikever;
    }
    if (!options->pike) {
      write("Cannot find Pike v%s in %s/bin/\n", options->pikever, getcwd());
      exit(1);
    }
  }

  // we're not specifying a pike version to use internally
  else {
    if(options->pike)
    {
      fstat=file_stat(options->pike);
      if(!(fstat && fstat->isreg && (fstat->mode & 0111)))
      {
        write("Specificed Pike %s does not exist or is not executable.\n", options->pike);
        exit(1);
      }
    }
    else
    {
      foreach(valid_interpreters, string vi)
      {
        fstat=file_stat(vi);
        if (fstat && fstat->isreg && (fstat->mode & 0111))
        {
          options->pike = vi;
          break;
        }
      }
    if(!options->pike || options->pike=="")
    {
      write("Unable to find a usable Pike interpreter.\n");
      exit(1);
    }

    }
  }

  os_rel = osdata->release / ".";
  if (sizeof(os_rel) < 2)
    os_rel += ({"0"}); // better this than nothing
  
  switch(osdata->sysname) {
      case "SunOS":
        if ((int)os_rel[0] >= 5 && (int)os_rel[1] >= 5) {
          os_label = "Solaris 2.5 or later";
          go_threads = 1;
        }
        break;

      case "FreeBSD":
        if ((int)os_rel[0] >= 4) {
          os_label = "FreeBSD 4.0 or later";
          go_threads = 1;
        }
        break;

      case "Linux":
        if ((int)os_rel[0] >= 2 && (int)os_rel[1] >= 2) {
          os_label = "Linux 2.2 or later";
          go_threads = 1;
        }
        break;

      case "Darwin":
        os_label = "Darwin or MacOS X";
        go_threads = 1;
        break;
  }

  if (go_threads && options->threads) {
    write("%s detected, enabling threads (if available in Pike)\n",
          os_label);
    command_line->DEFINES += ({"ENABLE_THREADS"});
  }

  command_line->DEFINES += ({"CAUDIUM", "CAUDIUM_CACHE", "ROXEN"});

  System.umask(022);

  if (!getenv("PIKE_NO_DEFAULT_PATHS")) {
    if (!getenv("PIKE_MASTER")) { // Pike default master program
//write(pikever + "\n");
//      array master_locations=({"lib/master.pike", "etc/caudium_master.pike"});
      array lib_locations=({"lib/modules", 
        "lib/pike/modules", "etc/modules", "lib/" + pikever,
        "share/pike/modules"});
      array program_locations=({getcwd()});
      array include_locations=({"lib/include", "etc/include", 
        "share/pike/include", "base_server"});

      command_line->OTHER += ({"-w"});
/*
      foreach(master_locations, string ml)
      {
         if(Stdio.is_file(ml))
         {
           command_line->OTHER += ({"-m" + ml});
         }
      }
*/
      foreach(include_locations, string il)
      {
         if(Stdio.is_dir(il))
         {
           command_line->INCLUDES += ({il});
           add_include_path(il);
         }
      }

      foreach(lib_locations, string ll)
      {
         if(Stdio.is_dir(ll))
         {
           command_line->MODULES += ({ll});
           add_module_path(ll);
         }
       }

      foreach(program_locations, string pl)
      {
         if(Stdio.is_dir(pl))
         {
           command_line->PROGRAMS += ({pl});
           add_program_path(pl);
         }
      }
   }
   else {
      command_line->OTHER += ({"-m" + getenv("PIKE_MASTER")});
    }
  }

  // extra paths
  dirs->extra_progpath += ({getcwd()});

  // a kludge for HPUX which doesn't like group 60001 (nobody)
  if (osdata->sysname == "HP-UX") {
    write("WARNING: applying a kludge for HPUX (see base_server/privs.pike)\n");
    command_line->DEFINES += ({"HPUX_KLUDGE"});
  }
}

array parse_arguments(array(string) argv)
{
  array(array)  parsed = Getopt.find_all_options(argv, arguments, 0);
  argv-=({0});
  if(sizeof(argv)>1) command_line->OTHER += argv[1..];
  return parsed;
}

string dirname(string p)
{
  array x=p/"/";
  if(sizeof(x)<2) return 0;
  x=x[0..(sizeof(x)-2)];
  p=x*"/";
  return p;
}

int main(int argc, array(string) argv)
{
  // first, change into the directory that start is living in.

  string d=dirname(argv[0]);
  if(d) cd(d);

  // is the directory we're in a valid caudium server root?
  if(!file_stat("base_server")) 
  {
    write("Cannot find Caudium server root\n");
    exit(1);
  }       

  parsed_args=parse_arguments(argv);

  int code=act_on_args();

  // do we get a "quit" code from the arg handler?
  if(code) return 0;

  preamble();

//  write("options: %O\n", options);
//  write("args: %O\ncommand_line: %O\nenvvars: %O\n", parsed_args, command_line, envvars);

  array o = generate_command_options();

  if(options->once)
  {
    mapping opt=([]);
    opt->stdout = Stdio.File("stdout");
    opt->stderr = Stdio.File("stderr");

    run_caudium(o, opt);
  }

  return 0;
}

array generate_command_options()
{
  array o=({});

  if(options->precmd)
    o+=options->precmd;
 
  o+=({options->pike});

  if(!options->precmd || options->precmd[0] != "gdb")
  {
    if(command_line->DEFINES)
      foreach(command_line->DEFINES, string d)
        o+=({"-D" + d});
    if(command_line->INCLUDES)
      foreach(command_line->INCLUDES, string d)
        o+=({"-I" + d});
    if(command_line->MODULES)
      foreach(command_line->MODULES, string d)
        o+=({"-M" + d});
    if(command_line->PROGRAMS)
      foreach(command_line->PROGRAMS, string d)
        o+=({"-P" + d});
    if(command_line->OTHER)
      foreach(command_line->OTHER, string d)
        o+=({d});

    o+=({options->program});
  }
  return o;
} 

int act_on_args()
{
  foreach(parsed_args, array m)
  {
    switch(m[0])
    {
      case "truss":
        options->precmd=({"truss"});
        options->once=1;
        continue;
      case "strace":
        options->precmd=({"strace", "-f"});
        options->once=1;
        continue;
      case "ltrace":
        options->precmd=({"ltrace", "-f"});
        options->once=1;
        continue;
      case "log-dir":
        envvars->CAUDIUM_LOGDIR=m[1];        
        continue;
      case "config-dir":
        envvars->CAUDIUM_CONFIGDIR=m[1];        
        continue;
      case "pike-version":
        options->pikever=m[1];
        continue;
      case "pid-file":
        envvars->CAUDIUM_PID_FILE=m[1];
        continue;
      case "help":
        write_help();
        return 1;
        continue;
      case "threads":
        options->threads=1;
        continue;
      case "nothreads":
        options->threads=0;
        continue;
      case "profile":
         command_line->DEFINES+=({"PROFILE"});
         continue;
      case "fileprofile":
         command_line->DEFINES+=({"FILE_PROFILE"});
         continue;
      case "once":
         options->once=1;
         continue;
      case "gdb":
         options->precmd=({"gdb"});
         options->once=1;
         continue;
      case "nodebug":
         command_line->DEFINES-=({"DEBUG", "MODULE_DEBUG", "CACHE_DEBUG"});
         continue;
      case "debug":
         command_line->DEFINES+=({"DEBUG", "MODULE_DEBUG", "CACHE_DEBUG"});
         continue;
      case "fddebug":
         command_line->DEFINES+=({"FD_DEBUG"});
         continue;
      case "keepalive":
        command_line->DEFINES+=({"KEEP_ALIVE"});
        continue;
      case "version":
         command_line->COTHER+=({"--version"});
         options->once=1;
         continue;
      case "pike":
        options->pike=m[1];
        continue;
      case "program":
        options->program=m[1];
        continue;
      case "quiet":
        options->quiet=1;
        continue;
    }
  return 0;
  }
}

void write_help()
{
   object ti=Stdio.Terminfo.getTerm();
   string bon=ti->tgetstr("md");
   string boff=ti->tgetstr("me");

   write( replace(
# ".BThis command will start the Caudium serverB..
The environment variable .BCAUDIUM_ARGSB. can be used to specify the
default arguments.
   .BArguments:B.
      .B--versionB.:  Output version information.
      .B--help -?B.:  This information
      .B--pike-version=VERB.:  Use an alternate pike version. For this to
				  work correctly, you need a
bin/caudium-VER
				  and the Caudium pike modules in
lib/VER/.
      .B--log-dir=DIRB.:  Set the log directory. Defaults to .B../logsB..
      .B--config-dir=DIRB.:  Use an alternate configuration directory
				  Defaults to .B../configurationB..
      .B--with-threadsB.:  If threads are available, use them.
      .B--without-threadsB.:  Even if threads are enabled by default,
                                  disable them.
      .B--with-profileB.:  Store runtime profiling information on
				  a directory basis. This information is
 				  not saved on permanent storage, it is
only
				  available until the next server restart
				  This will enable a new 'action' in the
				  configuration interface
      .B--with-file-profileB.:  Like .B--with-profileB., but save
information
                                  for each and every file.
      .B--with-keep-aliveB.:  Enable keep alive in the HTTP
			          protocol module. This will soon be
                                  the default. Some clients might have
				  problems with keepalive.
				  
      .B--onceB.:  Run the server only once, in the foreground.
			   	  This is very useful when debugging.
      .B--gdbB.:  Run the server in gdb. Implies .B--onceB..
      .B--programB.:  Start a different program with the caudium pike. As
an example,
                                  .B./start --program bin/install.pikeB.  
will
				  start the installation program normally
                                  started with .B./installB.
      .B--quietB.:  Run without normal debug output from the
                                  start script. Useful mainly when starting
				  other programs with --program.
      .B--with-debugB.:  Enable debug
      .B--without-debugB.:  Disable all debug
	
      .B--with-fd-debugB.:  Enable FD debug.
      .B--truss,--strace,--ltraceB.: Run the server under the selected tracer
                   		  program. This is extremely noisy, and is not
				  intented for anything but debugging purposes.
      .B--pid-file=<file>B.:  Store the caudium and startscript pids in this
				  file. Defaults to .B/tmp/caudium_\$UIDB.
         
  .BArguments passed to pike:B.
       .B-DDEFINEB.:  Define the symbol .BDEFINEB..
       .B-d<level>B.:  Set the runtime pike debug to level.
				  This only works if pike is compiled
				  with debug.
       .B-s<size>B.:  Set the stack size.
       .B-M <path>B.:  Add the path to the pike module path.
       .B-I <path>B.:  Add the path to the pike include path.
       .B-dtB.:  Turn of tail recursion optimization.
       .B-tB.:  Turn on pike level tracing.
       .B-t<level>B.:  Turn on more pike tracing. This only
				  works if pike is compiled with debug.
  .BEnvironment variables:B.
     .BCAUDIUM_CONFIGDIRB.:  Same as .B--config-dir=... B.
     .BCAUDIUM_PID_FILEB.:  Same as .B--pid-file=... B.
     .BCAUDIUM_LANGB.:  The default language for all language
				    related tags. Defaults to 'en' for english.
", ({".B", "B."}), ({bon, boff}))
);

}
@


1.2
log
@--once and about 2/3 of the options now work.
"...but what _value_ does it add?"
@
text
@d29 1
a29 1
 * $Id: start,v 1.1 2003/02/10 17:27:31 grendel Exp $
@


1.1
log
@wip area for caudium, and the first project - a new start script
@
text
@d25 1
d29 1
a29 1
 * $Id$
d33 1
a33 1
string pikever = sprintf("lib/%u.%u.%u/", __REAL_MAJOR__, __REAL_MINOR__, __REAL_BUILD__);
d35 1
a35 1
// argumentss we understand
a36 1
  ({"D", Getopt.HAS_ARG, ({"-D", "--define"})}),
d53 1
a56 2
  ({"pikeargs", Getopt.MAY_HAVE_ARG, ({"-r", "-d", "-t", "-l", "-w"})}),
  ({"pikedefines", Getopt.HAS_ARG, ({"-D", "-M", "-I", "-P"})}),
d58 1
a58 1
  ({"help", Getopt.NO_ARG, ({"--help"})})
d66 2
a67 8
  "program" : 0, 
]);

// files we load/use
// paths relative to the caudium server dir
mapping(string:string) files = ([
  "environment" : "etc/environment",
  "loader" : "base_server/caudiumloader.pike"
d75 1
a75 1
    "etc/modules/", pikever
d78 1
a78 1
    "etc/include", "base_server"
d94 6
d103 2
a104 1
  "INCLUDES" : ({})
d107 3
d136 4
a139 5
  opts->stdout = stdout;
  opts->stderr = stderr;
  opts->cwd = "/usr/src/tmp";

  proc = Process.create_process(({"/usr/bin/pike7.4", "-M /usr/src/tmp", "/usr/src/tmp/t.pike"}), opts);
d182 12
a193 5
  } else {
    if (!options->program) {
      fstat = file_stat("bin/caudium");
      if (fstat && fstat->isreg && (fstat->mode & 0111))
        options->pike = "bin/caudium";
d195 15
a209 4
    if (!options->pike) {
      fstat = file_stat("bin/pike");
      if (fstat && fstat->isreg && (fstat->mode & 0111))
        options->pike = "bin/pike";
d211 1
a211 3
    if (!options->pike) {
      write("Cannot find Pike in %s/bin/\n", getcwd());
      exit(1);
d247 2
a248 2
  if (go_threads) {
    write("%s detected, enabling threads by default (if available in Pike)\n",
d253 2
d259 26
a284 4
      // obsolete code, but kept for compatibility
      if (Stdio.is_dir("share/pike/")) {
        command_line->INCLUDES += ({"share/pike/include/"});
        append_env_path("PIKE_MODULE_PATH", "share/pike/modules/");
d287 16
a302 8
      if (Stdio.is_file("lib/master.pike")) {
        command_line->OTHER += ({"-mlib/master.pike"});
        command_line->INCLUDES += ({"lib/include/"});
        append_env_path("PIKE_MODULE_PATH", "lib/modules/");
      } else if (Stdio.is_file("lib/pike/master.pike")) {
        command_line->OTHER += ({"-mlib/pike/master.pike"});
        command_line->INCLUDES += ({"lib/pike/include/"});
        append_env_path("PIKE_MODULE_PATH", "lib/pike/modules/");
d304 2
a305 1
    } else {
d320 1
a320 1
void parse_arguments(array(string) argv)
d323 4
d328 7
d339 19
d360 128
a487 2
  write("command_line: %O\nenvvars: %O\n", command_line, envvars);
  
d489 84
@

