Roxen.git / server / base_server / roxenloader.pike

version» Context lines:

Roxen.git/server/base_server/roxenloader.pike:1:   // This file is part of Roxen WebServer.   // Copyright © 1996 - 2009, Roxen IS.   //   // Roxen bootstrap program.    - // $Id: roxenloader.pike,v 1.475 2012/02/29 13:13:52 grubba Exp $ + // $Id$      #define LocaleString Locale.DeferredLocale|string      mixed x = Calendar.Timezone; // #"!¤&"¤%/"&#¤!%#¤&#      // #pragma strict_types      // Sets up the roxen environment. Including custom functions like spawne().      #include <stat.h>   #include <roxen.h> -  +  + // --- Locale defines --- +  + //<locale-token project="roxen_start"> LOC_S </locale-token> + //<locale-token project="roxen_message"> LOC_M </locale-token> + #define LOC_S(X,Y) _STR_LOCALE("roxen_start",X,Y) + #define LOC_M(X,Y) _STR_LOCALE("roxen_message",X,Y) + #define CALL_M(X,Y) _LOCALE_FUN("roxen_message",X,Y) +    //   // NOTE:   // This file uses replace_master(). This implies that the   // master() efun when used in this file will return the old   // master and not the new one.   //   private __builtin.__master new_master;    -  + #if constant(spider) + // This ancient module has been removed in Pike 8.1.   constant s = spider; // compatibility -  + #endif      // Enable decoding of wide string data from mysql.   // Disabled since it isn't compatible enough - has to be enabled on a   // per-connection basis through the charset argument. /mast   //#define ENABLE_MYSQL_UNICODE_MODE      int remove_dumped;   string configuration_dir;   int once_mode;      #define werror roxen_perror    - constant cvs_version="$Id: roxenloader.pike,v 1.475 2012/02/29 13:13:52 grubba Exp $"; + constant cvs_version="$Id$";      int pid = getpid();   Stdio.File stderr = Stdio.File("stderr");      #if !constant(uname)   #ifdef __NT__   mapping uname()   {    return ([    "machine":"NT",
Roxen.git/server/base_server/roxenloader.pike:75:   #else    if(pwn[uid]) return pwn[uid];    return pwn[uid]=([array(string)]getpwuid(uid)||((""+uid)/":"))[0];   #endif   }      #if !constant(getppid)   int getppid() { return -1; }   #endif    - #if efun(syslog) + #if constant(syslog)   #define LOG_CONS (1<<0)   #define LOG_NDELAY (1<<1)   #define LOG_PERROR (1<<2)   #define LOG_PID (1<<3)      #define LOG_AUTH (1<<0)   #define LOG_AUTHPRIV (1<<1)   #define LOG_CRON (1<<2)   #define LOG_DAEMON (1<<3)   #define LOG_KERN (1<<4)
Roxen.git/server/base_server/roxenloader.pike:253:    if (co_rtime > 5.00) co_num_runs_5s++;    if (co_rtime > 15.00) co_num_runs_15s++;    co_acc_cpu_time += (int)(1E6*co_vtime);    co_acc_time += (int)(1E6*co_rtime);    if (err) throw(err);    return res;    }    }(f), delay, @args);   }    - protected int(0..5) last_was_change; - int(2..2147483647) roxen_started = [int(2..2147483647)]time(); - float roxen_started_flt = time(time()); +  + protected int(2..2147483647) roxen_started = [int(2..2147483647)]time(); + protected float roxen_started_flt = time(time()); + protected int uptime_row_counter; +    string short_time()   { -  if( last_was_change>0 ) -  switch( last_was_change-- ) -  { -  default: -  return " : "; -  case 5: -  float up = time(roxen_started)-roxen_started_flt; -  if( up > 3600 ) -  { -  return sprintf( "%2dd%2dh%2dm : ", -  (int)up/86400, -  (((int)up/3600)%24), -  ((int)up/60)%60); +  string up_str; +  if (uptime_row_counter) { +  up_str = " "; +  } else { +  float up = time(roxen_started) - roxen_started_flt; +  if (up > 3600) { +  up_str = sprintf( "%2dd%2dh%2dm", +  (int) up / 86400, +  (((int) up / 3600) % 24), +  ((int) up / 60) % 60); +  } else { +  up_str = sprintf( "%2dm%4.1fs ", ((int) up / 60) % 60, up % 60);    } -  return sprintf( "%2dm%4.1fs : ",((int)up/60)%60, up%60 ); +     } -  mapping l = localtime( time( ) ); -  string ct = sprintf("%2d:%02d:%02d : ", l->hour, l->min, l->sec ); -  last_was_change=5; -  return ct; +  uptime_row_counter = (uptime_row_counter + 1) % 5; +  +  mapping l = localtime(time()); +  return sprintf("%2d:%02d:%02d %s : ", l->hour, l->min, l->sec, up_str);   }    -  +    //! @decl void werror(string format, mixed ... args)   //! @appears werror      //! @decl void roxen_perror(string format, mixed ... args)   //! @appears roxen_perror      protected int last_was_nl = 1;   // Used to print error/debug messages   void roxen_perror(sprintf_format format, sprintf_args ... args)   {
Roxen.git/server/base_server/roxenloader.pike:325:    format = "";    if (delayed_nl) last_was_nl = -1;    } else {    stderr->write(string_to_utf8(format[..i]));    format = format[i+1..];    last_was_nl = 1;    }    }       if (sizeof(format)) { - #if efun(syslog) + #if constant(syslog)    syslog_report (format, LOG_DEBUG);   #endif       if (last_was_nl == -1) stderr->write("\n");    last_was_nl = format[-1] == '\n';      #ifdef RUN_SELF_TEST    stderr->write( string_to_utf8( format ) );   #else    array(string) a = format/"\n";    int i;       a = map( a, string_to_utf8 );    -  + #ifdef DEBUG_LOG_SHOW_USER +  string usr; +  catch { +  mixed rxml_ctx = all_constants()["_cur_rxml_context"]->get(); +  if(rxml_ctx) +  usr = rxml_ctx->user_get_var("user.username"); +  }; + #endif +     for(i=0; i < sizeof(a)-1; i++) { -  + #ifdef DEBUG_LOG_SHOW_USER +  if(usr) +  a[i] = usr + " : " + a[i]; + #endif    stderr->write(short_time() + a[i] + "\n");    }    if (!last_was_nl) { -  + #ifdef DEBUG_LOG_SHOW_USER +  if(usr) +  a[-1] = usr + " : " + a[-1]; + #endif    stderr->write(short_time() + a[-1]);    }   #endif    }       if (delayed_nl) last_was_nl = -1;   }      //! @appears mkdirhier   //! Make a directory hierachy
Roxen.git/server/base_server/roxenloader.pike:416:   mixed query(string arg)   {    if(!roxen)    error("No roxen object!\n");    return roxen->query( arg );   }      // used for debug messages. Sent to the administration interface and STDERR.   void init_logger()   { - #if efun(syslog) + #if constant(syslog)    int res;    use_syslog = !! (query("LogA") == "syslog");       switch(query("LogST"))    {    case "Daemon": res = LOG_DAEMON; break;    case "Local 0": res = LOG_LOCAL; break;    case "Local 1": res = LOG_LOCAL1; break;    case "Local 2": res = LOG_LOCAL2; break;    case "Local 3": res = LOG_LOCAL3; break;
Roxen.git/server/base_server/roxenloader.pike:493:    if( !conf ) conf = o;    }    if( conf )    break;    }    return ({ mod,conf });   }      protected void syslog_report (string message, int level)   { - #if efun(syslog) + #if constant(syslog)    if(use_syslog && (loggingfield&level))    foreach(message/"\n", message)    syslog(level, replace(message+"\n", "%", "%%"));   #endif   }         #define MC @find_module_and_conf_for_log(backtrace())      void report_warning(LocaleString|sprintf_format message, sprintf_args ... foo)   //! @appears report_warning   //! Report a warning message, that will show up in the server's debug log and   //! in the event logs, along with the yellow exclamation mark warning sign.   //! Shares argument prototype with @[sprintf()].   //!   //! @seealso   //! @[report_warning_sparsely], @[report_warning_for]   {    if( sizeof( foo ) ) message = sprintf((string)message, @foo );    nwrite([string]message,0,2,MC); - #if efun(syslog) + #if constant(syslog)    if (use_syslog) syslog_report (message, LOG_WARNING);   #endif   }      void report_notice(LocaleString|sprintf_format message, sprintf_args ... foo)   //! @appears report_notice   //! Report a status message of some sort for the server's debug log and event   //! logs, along with the blue informational notification sign. Shares argument   //! prototype with @[sprintf()].   //!   //! @seealso   //! @[report_notice_for]   {    if( sizeof( foo ) ) message = sprintf((string)message, @foo );    nwrite([string]message,0,1,MC); - #if efun(syslog) + #if constant(syslog)    if (use_syslog) syslog_report (message, LOG_NOTICE);   #endif   }      void report_error(LocaleString|sprintf_format message, sprintf_args ... foo)   //! @appears report_error   //! Report an error message, that will show up in the server's debug log and   //! in the event logs, along with the red exclamation mark sign. Shares   //! argument prototype with @[sprintf()].   //!   //! @seealso   //! @[report_error_sparsely], @[report_error_for]   {    if( sizeof( foo ) ) message = sprintf((string)message, @foo );    nwrite([string]message,0,3,MC); - #if efun(syslog) + #if constant(syslog)    if (use_syslog) syslog_report (message, LOG_ERR);   #endif   }      void report_fatal(sprintf_format message, sprintf_args ... foo)   //! @appears report_fatal   //! Print a fatal error message.   //!   //! @seealso   //! @[report_fatal_for]   {    if( sizeof( foo ) ) message = sprintf((string)message, @foo );    nwrite(message,0,3,MC); - #if efun(syslog) + #if constant(syslog)    if (use_syslog) syslog_report (message, LOG_EMERG);   #endif   }      void report_warning_for (object/*(Configuration|RoxenModule)*/ where,    LocaleString|sprintf_format message,    sprintf_args ... args)   //! @appears report_warning_for   //! See @[report_error_for].   {    if (sizeof (args)) message = sprintf (message, @args);    nwrite (message, 0, 2,    where && where->is_module && where,    where && where->is_configuration && where); - #if efun(syslog) + #if constant(syslog)    if (use_syslog) syslog_report (message, LOG_WARNING);   #endif   }      void report_notice_for (object/*(Configuration|RoxenModule)*/ where,    LocaleString|sprintf_format message,    sprintf_args ... args)   //! @appears report_notice_for   //! See @[report_error_for].   {    if (sizeof (args)) message = sprintf (message, @args);    nwrite (message, 0, 1,    where && where->is_module && where,    where && where->is_configuration && where); - #if efun(syslog) + #if constant(syslog)    if (use_syslog) syslog_report (message, LOG_NOTICE);   #endif   }      void report_error_for (object/*(Configuration|RoxenModule)*/ where,    LocaleString|sprintf_format message,    sprintf_args ... args)   //! @appears report_error_for   //! Like @[report_error], but logs the message for the given   //! configuration or Roxen module @[where], or globally if @[where] is   //! zero. @[report_error] searches the call stack to find that out,   //! but this function is useful to specify it explicitly.   {    if (sizeof (args)) message = sprintf (message, @args);    nwrite (message, 0, 3,    where && where->is_module && where,    where && where->is_configuration && where); - #if efun(syslog) + #if constant(syslog)    if (use_syslog) syslog_report (message, LOG_ERR);   #endif   }      void report_fatal_for (object/*(Configuration|RoxenModule)*/ where,    LocaleString|sprintf_format message,    sprintf_args ... args)   //! @appears report_fatal_for   //! See @[report_error_for].   {    if (sizeof (args)) message = sprintf (message, @args);    nwrite (message, 0, 3,    where && where->is_module && where,    where && where->is_configuration && where); - #if efun(syslog) + #if constant(syslog)    if (use_syslog) syslog_report (message, LOG_EMERG);   #endif   }      protected mapping(string:int) sparsely_dont_log = (garb_sparsely_dont_log(), ([]));      protected void garb_sparsely_dont_log()   {    if (sparsely_dont_log && sizeof (sparsely_dont_log)) {    int now = time (1); -  foreach (indices (sparsely_dont_log), string msg) -  if (sparsely_dont_log[msg] < now) m_delete (sparsely_dont_log, msg); +  foreach (sparsely_dont_log; string msg; int ts) +  if (ts < now) m_delete (sparsely_dont_log, msg);    } -  call_out (garb_sparsely_dont_log, 10*60); +  call_out (garb_sparsely_dont_log, 20*60);   }      void report_warning_sparsely (LocaleString|sprintf_format message,    sprintf_args ... args)   //! @appears report_warning_sparsely   //! Like @[report_warning], but doesn't repeat the same message if   //! it's been logged in the last ten minutes. Useful in situations   //! where an error can cause a warning message to be logged rapidly.   {    if( sizeof( args ) ) message = sprintf((string)message, @args );    int now = time (1);    if (sparsely_dont_log[message] >= now) return;    sparsely_dont_log[message] = now + 10*60;    nwrite([string]message,0,2,MC); - #if efun(syslog) + #if constant(syslog)    if (use_syslog) syslog_report (message, LOG_WARNING);   #endif   }      void report_error_sparsely (LocaleString|sprintf_format message,    sprintf_args ... args)   //! @appears report_error_sparsely   //! Like @[report_error], but doesn't repeat the same message if it's   //! been logged in the last ten minutes. Useful in situations where an   //! error can cause an error message to be logged rapidly.   {    if( sizeof( args ) ) message = sprintf((string)message, @args );    int now = time (1); -  if (sparsely_dont_log[message] >= now - 10*60*60) return; -  sparsely_dont_log[message] = now; +  if (sparsely_dont_log[message] >= now) return; +  sparsely_dont_log[message] = now + 10*60;    nwrite([string]message,0,3,MC); - #if efun(syslog) + #if constant(syslog)    if (use_syslog) syslog_report (message, LOG_ERR);   #endif   }      //! @appears popen   //! Starts the specified process and returns a string   //! with the result. Mostly a compatibility functions, uses   //! Process.Process   //!   //! If @[cmd] is a string then it's interpreted as a command line with
Roxen.git/server/base_server/roxenloader.pike:738:   }      //! @appears spawne   //! Create a process   Process.Process spawne(string s, array(string) args, mapping|array env,    Stdio.File stdin, Stdio.File stdout, Stdio.File stderr,    void|string wd, void|array(int) uid)   {    int u, g;    if(uid) { u = uid[0]; g = uid[1]; } - #if efun(geteuid) + #if constant(geteuid)    else { u=geteuid(); g=getegid(); }   #endif    return Process.Process(({s}) + (args || ({})), ([    "toggle_uid":1,    "stdin":stdin,    "stdout":stdout,    "stderr":stderr,    "cwd":wd,    "env":env,    "uid":u,
Roxen.git/server/base_server/roxenloader.pike:791:    object cache;    cache=((program)"base_server/cache")();       add_constant("http_decode_string", _Roxen.http_decode_string );    add_constant("cache_clear_deltas", cache->cache_clear_deltas);    add_constant("cache_set", cache->cache_set);    add_constant("cache_lookup", cache->cache_lookup);    add_constant("cache_peek", cache->cache_peek);    add_constant("cache_remove", cache->cache_remove);    add_constant("cache_expire", cache->cache_expire); +  add_constant("cache_expire_by_prefix", cache->cache_expire_by_prefix);    add_constant("cache_clear", cache->cache_expire);    add_constant("cache_entries",cache->cache_entries);    add_constant("cache_indices",cache->cache_indices);    add_constant("CacheEntry", cache->CacheEntry); // For cache_entries typing.       return cache;   }      class _error_handler {    void compile_error(string a,int b,string c);
Roxen.git/server/base_server/roxenloader.pike:906:    return cd(path);    }   }      // Fallback efuns.   #if !constant(getuid)   int getuid(){ return 17; }   int getgid(){ return 42; }   #endif    + #if constant(Crypto.Password) + // Pike 7.9 and later. + constant verify_password = Crypto.Password.verify; + constant crypt_password = Crypto.Password.hash; +  + #else /* !Crypto.Password */ +  + //! @appears verify_password + //! + //! Verify a password against a hash. + //! + //! This function attempts to support most + //! password hashing schemes. + //! + //! @returns + //! Returns @expr{1@} on success, and @expr{0@} (zero) otherwise. + //! + //! @seealso + //! @[hash_password()], @[predef::crypt()] + int verify_password(string password, string hash) + { +  if (hash == "") return 1; +  +  // Detect the password hashing scheme. +  // First check for an LDAP-style marker. +  string scheme = "crypt"; +  sscanf(hash, "{%s}%s", scheme, hash); +  // NB: RFC2307 proscribes lower case schemes, while +  // in practise they are usually in upper case. +  switch(lower_case(scheme)) { +  case "md5": // RFC 2307 +  case "smd5": +  hash = MIME.decode_base64(hash); +  password += hash[16..]; +  hash = hash[..15]; +  return Crypto.MD5.hash(password) == hash; +  +  case "sha": // RFC 2307 +  case "ssha": +  // SHA1 and Salted SHA1. +  hash = MIME.decode_base64(hash); +  password += hash[20..]; +  hash = hash[..19]; +  return Crypto.SHA1.hash(password) == hash; +  +  case "crypt": // RFC 2307 +  // First try the operating system's crypt(3C). +  if ((hash == "") || crypt(password, hash)) return 1; +  if (hash[0] != '$') { +  if (hash[0] == '_') { +  // FIXME: BSDI-style crypt(3C). +  } +  return 0; +  } +  +  // Then try our implementations. +  sscanf(hash, "$%s$%s$%s", scheme, string salt, string hash); +  int rounds = UNDEFINED; +  if (has_prefix(salt, "rounds=")) { +  sscanf(salt, "rounds=%d", rounds); +  sscanf(hash, "%s$%s", salt, hash); +  } +  switch(scheme) { +  case "1": // crypt_md5 +  return Nettle.crypt_md5(password, salt) == hash; +  +  case "2": // Blowfish (obsolete) +  case "2a": // Blowfish (possibly weak) +  case "2x": // Blowfish (weak) +  case "2y": // Blowfish (stronger) +  break; +  +  case "3": // MD4 NT LANMANAGER (FreeBSD) +  break; +  + #if constant(Crypto.SHA256.crypt_hash) +  // cf http://www.akkadia.org/drepper/SHA-crypt.txt +  case "5": // SHA-256 +  return Crypto.SHA256.crypt_hash(password, salt, rounds) == hash; + #endif + #if constant(Crypto.SHA512.crypt_hash) +  case "6": // SHA-512 +  return Crypto.SHA512.crypt_hash(password, salt, rounds) == hash; + #endif +  } +  break; +  } +  return 0; + } +  + //! @appears crypt_password + //! + //! Generate a hash of @[password] suitable for @[verify_password()]. + //! + //! @param password + //! Password to hash. + //! + //! @param scheme + //! Password hashing scheme. If not specified the strongest available + //! will be used. + //! + //! If an unsupported scheme is specified an error will be thrown. + //! + //! @param rounds + //! The number of rounds to use in parameterized schemes. If not + //! specified the scheme specific default will be used. + //! + //! @returns + //! Returns a string suitable for @[verify_password()]. + //! + //! @seealso + //! @[verify_password], @[predef::crypt()], @[Nettle.crypt_md5()], + //! @[Nettle.HashInfo()->crypt_hash()] + string crypt_password(string password, string|void scheme, int|void rounds) + { +  function(string, string, int:string) crypt_hash; +  int salt_size = 16; +  int default_rounds = 5000; +  switch(scheme) { +  case UNDEFINED: +  // FALL_THROUGH + #if constant(Crypto.SHA512.crypt_hash) +  case "6": +  case "$6$": +  crypt_hash = Crypto.SHA512.crypt_hash; +  scheme = "6"; +  break; + #endif + #if constant(Crypto.SHA256.crypt_hash) +  case "5": +  case "$5$": +  crypt_hash = Crypto.SHA256.crypt_hash; +  scheme = "5"; +  break; + #endif + #if constant(Crypto.MD5.crypt_hash) +  case "1": +  case "$1$": +  crypt_hash = Crypto.MD5.crypt_hash; +  salt_size = 8; +  rounds = 1000; // Currently only 1000 rounds is supported. +  default_rounds = 1000; +  scheme = "1"; +  break; + #endif +  case "": +  return crypt(password); +  // FIXME: Add support for SSHA? +  default: +  error("Unsupported hashing scheme: %O\n", scheme); +  } +  +  if (!rounds) rounds = default_rounds; +  +  // NB: The salt must be printable. +  string salt = +  MIME.encode_base64(Crypto.Random.random_string(salt_size))[..salt_size-1]; +  +  string hash = crypt_hash(password, salt, rounds); +  +  if (rounds != default_rounds) { +  salt = "rounds=" + rounds + "$" + salt; +  } +  +  return sprintf("$%s$%s$%s", scheme, salt, hash); + } + #endif /* !Crypto.Password */ +  + #ifdef THREADS + // This mutex is used by Privs + Thread.Mutex euid_egid_lock = Thread.Mutex(); + #endif /* THREADS */ +  + // Needed to get core dumps of seteuid()'ed processes on Linux. + #if constant(System.dumpable) + #define enable_coredumps(X) System.dumpable(X) + #else + #define enable_coredumps(X) + #endif +  + /* +  * The privilege changer. Works like a mutex lock, but changes the UID/GID +  * while held. Blocks all threads. +  * +  * Based on privs.pike,v 1.36. +  */ + int privs_level; +  + protected class Privs + { + #if constant(seteuid) +  +  int saved_uid; +  int saved_gid; +  +  int new_uid; +  int new_gid; +  + #define LOGP (roxen->variables && roxen->variables->audit && roxen->variables->audit->query()) +  + #if constant(geteuid) && constant(getegid) && constant(seteuid) && constant(setegid) + #define HAVE_EFFECTIVE_USER + #endif +  +  private string _getcwd() +  { +  if (catch{return(getcwd());}) { +  return("Unknown directory (no x-bit on current directory?)"); +  } +  } +  +  private string dbt(array t) +  { +  if(!arrayp(t) || (sizeof(t)<2)) return ""; +  return (((t[0]||"Unknown program")-(_getcwd()+"/"))-"base_server/")+":"+t[1]+"\n"; +  } +  + #ifdef THREADS +  protected mixed mutex_key; // Only one thread may modify the euid/egid at a time. +  protected object threads_disabled; + #endif /* THREADS */ +  +  int p_level; +  +  void create(string reason, int|string|void uid, int|string|void gid) +  { +  // No need for Privs if the uid has been changed permanently. +  if(getuid()) return; +  + #ifdef PRIVS_DEBUG +  report_debug(sprintf("Privs(%O, %O, %O)\n" +  "privs_level: %O\n", +  reason, uid, gid, privs_level)); + #endif /* PRIVS_DEBUG */ +  + #ifdef HAVE_EFFECTIVE_USER +  array u; +  + #ifdef THREADS +  if (euid_egid_lock) { +  if (mixed err = catch { mutex_key = euid_egid_lock->lock(); }) +  master()->handle_error (err); +  } +  threads_disabled = _disable_threads(); + #endif /* THREADS */ +  +  p_level = privs_level++; +  +  /* Needs to be here since root-priviliges may be needed to +  * use getpw{uid,nam}. +  */ +  saved_uid = geteuid(); +  saved_gid = getegid(); +  seteuid(0); +  +  /* A string of digits? */ +  if(stringp(uid) && (replace(uid,"0123456789"/"",({""})*10)=="")) +  uid = (int)uid; +  +  if(stringp(gid) && (replace(gid, "0123456789"/"", ({"" })*10) == "")) +  gid = (int)gid; +  +  if(!stringp(uid)) +  u = getpwuid(uid); +  else +  { +  u = getpwnam(uid); +  if(u) +  uid = u[2]; +  } +  +  if(u && !gid) +  gid = u[3]; +  +  if(!u) +  { +  if (uid && (uid != "root")) +  { +  if (intp(uid) && (uid >= 60000)) +  { +  report_warning(sprintf("Privs: User %d is not in the password database.\n" +  "Assuming nobody.\n", uid)); +  // Nobody. +  gid = gid || uid; // Fake a gid also. +  u = ({ "fake-nobody", "x", uid, gid, "A real nobody", "/", "/sbin/sh" }); +  } else { +  error("Unknown user: "+uid+"\n"); +  } +  } else { +  u = ({ "root", "x", 0, gid, "The super-user", "/", "/sbin/sh" }); +  } +  } +  +  if(LOGP) +  report_notice(LOC_M(1, "Change to %s(%d):%d privs wanted (%s), from %s"), +  (string)u[0], (int)uid, (int)gid, +  (string)reason, +  (string)dbt(backtrace()[-2])); +  +  if (u[2]) { + #if constant(cleargroups) +  if (mixed err = catch { cleargroups(); }) +  master()->handle_error (err); + #endif /* cleargroups */ + #if constant(initgroups) +  if (mixed err = catch { initgroups(u[0], u[3]); }) +  master()->handle_error (err); + #endif +  } +  gid = gid || getgid(); +  int err = (int)setegid(new_gid = gid); +  if (err < 0) { +  report_warning(LOC_M(2, "Privs: WARNING: Failed to set the " +  "effective group id to %d!\n" +  "Check that your password database is correct " +  "for user %s(%d),\n and that your group " +  "database is correct.\n"), +  gid, (string)u[0], (int)uid); +  int gid2 = gid; + #ifdef HPUX_KLUDGE +  if (gid >= 60000) { +  /* HPUX has doesn't like groups higher than 60000, +  * but has assigned nobody to group 60001 (which isn't even +  * in /etc/group!). +  * +  * HPUX's libc also insists on filling numeric fields it doesn't like +  * with the value 60001! +  */ +  report_debug("Privs: WARNING: Assuming nobody-group.\n" +  "Trying some alternatives...\n"); +  // Assume we want the nobody group, and try a couple of alternatives +  foreach(({ 60001, 65534, -2 }), gid2) { +  report_debug("%d... ", gid2); +  if (initgroups(u[0], gid2) >= 0) { +  if ((err = setegid(new_gid = gid2)) >= 0) { +  report_debug("Success!\n"); +  break; +  } +  } +  } +  } + #endif /* HPUX_KLUDGE */ +  if (err < 0) { +  report_debug("Privs: Failed\n"); +  error ("Failed to set EGID to %d\n", gid); +  } +  report_debug("Privs: WARNING: Set egid to %d instead of %d.\n", +  gid2, gid); +  gid = gid2; +  } +  if(getgid()!=gid) setgid(gid||getgid()); +  seteuid(new_uid = uid); +  enable_coredumps(1); + #endif /* HAVE_EFFECTIVE_USER */ +  } +  +  void destroy() +  { +  // No need for Privs if the uid has been changed permanently. +  if(getuid()) return; +  + #ifdef PRIVS_DEBUG +  report_debug(sprintf("Privs->destroy()\n" +  "privs_level: %O\n", +  privs_level)); + #endif /* PRIVS_DEBUG */ +  + #ifdef HAVE_EFFECTIVE_USER +  /* Check that we don't increase the privs level */ +  if (p_level >= privs_level) { +  report_error(sprintf("Change back to uid#%d gid#%d from uid#%d gid#%d\n" +  "in wrong order! Saved level:%d Current level:%d\n" +  "Occurs in:\n%s\n", +  saved_uid, saved_gid, new_uid, new_gid, +  p_level, privs_level, +  describe_backtrace(backtrace()))); +  return(0); +  } +  if (p_level != privs_level-1) { +  report_error(sprintf("Change back to uid#%d gid#%d from uid#%d gid#%d\n" +  "Skips privs level. Saved level:%d Current level:%d\n" +  "Occurs in:\n%s\n", +  saved_uid, saved_gid, new_uid, new_gid, +  p_level, privs_level, +  describe_backtrace(backtrace()))); +  } +  privs_level = p_level; +  +  if(LOGP) { +  if (mixed err = catch { +  array bt = backtrace(); +  if (sizeof(bt) >= 2) { +  report_notice(LOC_M(3,"Change back to uid#%d gid#%d, from %s")+"\n", +  saved_uid, saved_gid, dbt(bt[-2])); +  } else { +  report_notice(LOC_M(4,"Change back to uid#%d gid#%d, " +  "from backend")+"\n", saved_uid, saved_gid); +  } +  }) +  master()->handle_error (err); +  } +  + #ifdef PRIVS_DEBUG +  int uid = geteuid(); +  if (uid != new_uid) { +  report_debug("Privs: UID #%d differs from expected #%d\n" +  "%s\n", +  uid, new_uid, describe_backtrace(backtrace())); +  } +  int gid = getegid(); +  if (gid != new_gid) { +  report_debug("Privs: GID #%d differs from expected #%d\n" +  "%s\n", +  gid, new_gid, describe_backtrace(backtrace())); +  } + #endif /* PRIVS_DEBUG */ +  +  seteuid(0); +  array u = getpwuid(saved_uid); + #if constant(cleargroups) +  if (mixed err = catch { cleargroups(); }) +  master()->handle_error (err); + #endif /* cleargroups */ +  if(u && (sizeof(u) > 3)) { +  if (mixed err = catch { initgroups(u[0], u[3]); }) +  master()->handle_error (err); +  } +  setegid(saved_gid); +  seteuid(saved_uid); +  enable_coredumps(1); + #endif /* HAVE_EFFECTIVE_USER */ +  } + #else /* constant(seteuid) */ +  void create(string reason, int|string|void uid, int|string|void gid){} + #endif /* constant(seteuid) */ + } +    // Load Roxen for real   Roxen really_load_roxen()   {    int start_time = gethrtime();    report_debug("Loading Roxen ... \b");    Roxen res;    mixed err = catch {    res = ((program)"base_server/roxen.pike")();    };    if (err)
Roxen.git/server/base_server/roxenloader.pike:1174:   constant mf = Stdio.File;   #endif      #include "../etc/include/version.h"      protected string release;   protected string dist_version;   protected string dist_os;   protected int roxen_is_cms;   protected string roxen_product_name; + protected string roxen_product_code;    -  + protected string mysql_product_name; + protected string mysql_version; +  + protected constant mysql_good_versions = ({ "5.5.*", "5.6.*" }); + protected constant mariadb_good_versions = ({ "5.5.*", "10.0.*", "10.1.*" }); + protected constant mysql_maybe_versions = ({ "5.*", "6.*" }); + protected constant mariadb_maybe_versions = ({ "5.*", "10.*", "11.*" }); +    string roxen_version()   //! @appears roxen_version   {    // Note: roxen_release is usually "-cvs" at the time this is compiled.    return roxen_ver+"."+roxen_build+(release||roxen_release);   }      //! @appears roxen_path   //!   //! Expands the following paths in the given string and returns the
Roxen.git/server/base_server/roxenloader.pike:1340:   }      array(string) default_roxen_font_path =    ({ "nfonts/",   #ifdef __NT__    combine_path(replace(getenv("SystemRoot"), "\\", "/"), "fonts/")   #else    @((getenv("RX_FONTPATH") || "")/"," - ({""}))   #endif    }); - array(string) default_roxen_module_path = ({ "modules/" }); + array(string) package_module_path = ({ });      array(string) package_directories = ({ });      void add_package(string package_dir)   {    string ver = r_read_bytes(combine_path(package_dir, "VERSION"));    if (ver && (ver != "")) {    report_debug("Adding package %s (Version %s).\n",    roxen_path (package_dir), ver - "\n");    } else {    report_debug("Adding package %s.\n",    roxen_path (package_dir));    } -  package_directories = ({ package_dir }) + package_directories; +  package_directories += ({ package_dir });       string real_pkg_dir = roxen_path (package_dir);    string sub_dir = combine_path(real_pkg_dir, "pike-modules");    if (Stdio.is_dir(sub_dir)) {    master()->add_module_path(sub_dir);    }    if (Stdio.is_dir(sub_dir = combine_path(real_pkg_dir, "include/"))) {    master()->add_include_path(sub_dir);    }    -  default_roxen_module_path = ({ combine_path(package_dir, "modules/") }) + -  default_roxen_module_path; +  package_module_path += ({ combine_path(package_dir, "modules/") });    if (r_is_dir(sub_dir = combine_path(package_dir, "roxen-modules/"))) { -  default_roxen_module_path = ({ sub_dir }) + default_roxen_module_path; +  package_module_path += ({ sub_dir });    }    if (r_is_dir(sub_dir = combine_path(package_dir, "fonts/"))) { -  default_roxen_font_path = ({ sub_dir }) + default_roxen_font_path; +  default_roxen_font_path += ({ sub_dir });    }   #ifdef RUN_SELF_TEST    if (r_is_dir(sub_dir = combine_path(package_dir, "test/modules/"))) {    package_module_path += ({ sub_dir });    }   #endif   }         //! @appears lopen   object|void lopen(string filename, string mode, int|void perm)   {    if( filename[0] != '/' ) {    foreach(package_directories, string dir) {    Stdio.File o; -  if (o = open(combine_path(dir, filename), mode, perm)) return o; +  if (o = open(combine_path(roxen_path(dir), filename), mode, perm)) +  return o;    }    }    return open( filename, mode, perm );   }      //! @appears lfile_stat   object(Stdio.Stat) lfile_stat(string filename)   {    if (filename[0] != '/') {    foreach(package_directories, string dir) {    Stdio.Stat res; -  if (res = file_stat(combine_path(dir, filename))) return res; +  if (res = file_stat(combine_path(roxen_path(dir), filename))) +  return res;    }    }    return file_stat(filename);   }      //! @appears lfile_path   string lfile_path(string filename)   {    if (filename[0] != '/') {    foreach(package_directories, string dir) { -  string path = combine_path(dir, filename); +  string path = combine_path(roxen_path(dir), filename);    if (file_stat(path)) return path;    }    }    return file_stat(filename) && filename;   }      // Make a $PATH-style string   string make_path(string ... from)   {    return map(from, lambda(string a, string b) {
Roxen.git/server/base_server/roxenloader.pike:1447:    if( !roxen )    {    call_out( write_current_time, 10 );    return;    }    int t = time(1);    mapping lt = localtime(t);    report_debug("\n** "+sprintf("%02d-%02d-%02d %02d:%02d", lt->year+1900,    lt->mon+1, lt->mday, lt->hour, lt->min)+    " pid: "+pid+" ppid: "+getppid()+ - #if efun(geteuid) + #if constant(geteuid)    (geteuid()!=getuid()?" euid: "+pw_name(geteuid()):"")+   #endif    " uid: "+pw_name(getuid())+"\n\n");    call_out( write_current_time, 3600 - t % 3600 );   }      //! @appears throw   //! Overloads Pikes throw function.   //!   //! Exists for detection of code that throws non-errors.
Roxen.git/server/base_server/roxenloader.pike:1474:    report_debug(sprintf("Warning: throwing non-error: %O\n"    "From: %s\n",    err, describe_backtrace(backtrace())));    }    throw(err);   }      // Roxen bootstrap code.   int main(int argc, array(string) argv)   { -  // For Pike 7.3 -  add_constant("__pragma_save_parent__",1); // FIXME: Change this later on +     Protocols.HTTP; // FIXME: Workaround for bug 2637.    - #if __VERSION__ < 7.8 + #if __VERSION__ < 8.0    report_debug(   #"   ------- FATAL ------------------------------------------------- - Roxen 5.0 should be run with Pike 7.8 or newer. + Roxen 6.0 should be run with Pike 8.0 or newer.   ---------------------------------------------------------------   ");    exit(1);   #endif       // Check if IPv6 support is available.    if (mixed err = catch {    // Note: Attempt to open a port on the IPv6 loopback (::1)    // rather than on IPv6 any (::), to make sure some    // IPv6 support is actually configured. This is needed
Roxen.git/server/base_server/roxenloader.pike:1547:    dist_version = roxen_version();       // Get build OS for dist    dist_os =    (replace(Stdio.read_bytes("OS") || "src dist", "\r", "\n") / "\n")[0];       // Get package directories.    add_package("$LOCALDIR");    foreach(package_directories + ({ "." }), string dir) {    dir = combine_path(dir, "packages"); -  foreach(sort(get_dir(dir) || ({})), string fname) { +  foreach(sort(get_dir(roxen_path(dir)) || ({})), string fname) {    if (fname == "CVS") continue;    fname = combine_path(dir, fname); -  if (Stdio.is_dir(fname)) { +  if (Stdio.is_dir(roxen_path(fname))) {    add_package(fname);    }    }    }    -  roxen_is_cms = !!lfile_stat("modules/sitebuilder"); +  roxen_is_cms = !!lfile_stat("modules/sitebuilder") || +  !!lfile_stat("packages/sitebuilder");    -  if(roxen_is_cms) +  if(roxen_is_cms) { +  if (lfile_stat("modules/print") || lfile_stat("packages/print")) { +  roxen_product_name="Roxen EP"; +  roxen_product_code = "rep"; +  } else {    roxen_product_name="Roxen CMS"; -  else +  roxen_product_code = "cms"; +  } +  } else {    roxen_product_name="Roxen WebServer"; -  +  roxen_product_code = "webserver"; +  }      #if defined(ROXEN_USE_FORKD) && constant(Process.set_forkd_default)    report_debug("Enabling use of forkd daemon.\n");    Process.set_forkd_default(1);   #endif       // The default (internally managed) mysql path    string defpath =   #ifdef __NT__    // Use pipes with a name created from the config dir -  "mysql://%user%@.:"+ -  replace(combine_path(query_mysql_data_dir(), "pipe"), ":", "_") + -  "/%db%"; +  "mysql://%user%@.:" + query_mysql_socket() + "/%db%";   #else -  "mysql://%user%@localhost:"+ -  combine_path(query_mysql_data_dir(), "socket")+ -  "/%db%"; +  "mysql://%user%@localhost:" + query_mysql_socket() + "/%db%";   #endif       my_mysql_path =    Getopt.find_option(av, "m",({"mysql-url", }), 0,defpath);       if( my_mysql_path != defpath )    {    werror(    " : ----------------------------------------------------------\n"    "Notice: Not using the built-in MySQL\n"
Roxen.git/server/base_server/roxenloader.pike:1623:    trace_exit(1);   }         protected mapping(string:string) cached_mysql_location;      //! Returns a mapping with the following MySQL-related paths:   //!   //! @code   //! ([ - //! "basedir" : <absolute path to MySQL server directory> - //! "mysqld" : <absolute path to mysqld[-nt.exe]> - //! "myisamchk" : <absolute path to myisamchk[.exe]> - //! "mysqldump" : <absolute path to mysqldump[.exe]> + //! "basedir" : <absolute path to MySQL server directory> + //! "mysqld" : <absolute path to mysqld[-nt.exe]> + //! "myisamchk" : <absolute path to myisamchk[.exe]> + //! "mysqldump" : <absolute path to mysqldump[.exe]> + //! "mysql_upgrade" : <absolute path to mysql_upgrade[.exe]>   //! ])   //! @endcode   //!   //! If a path cannot be resolved it will be set to 0.   //!   //! The paths are read from "mysql-location.txt" in the server-x.y.z   //! directory. If that file doesn't exist then default values based   //! on the server-x.y.z/mysql/ subdirectory will be substituted.   //!   //! @note
Roxen.git/server/base_server/roxenloader.pike:1654:    {    foreach(paths, string p)    if (file_stat(p))    return p;    return 0;    };       multiset(string) valid_keys =    // NOTE: "mysqladmin" not used but listed here since NT starter    // looks for it. -  (< "basedir", "mysqld", "myisamchk", "mysqladmin", "mysqldump" >); +  (< "basedir", "mysqld", "myisamchk", "mysqladmin", "mysqldump", +  "mysql_upgrade", +  >);       // If the path file is missing we fall back on the traditional    // /mysql/ subdirectory. The file should contain lines on this    // format:    //    // # comment    // key1 = value    // key2 = value    //    // All non-absolute paths will be interpreted relative to server-x.y.z.
Roxen.git/server/base_server/roxenloader.pike:1742: Inside #if defined(__NT__)
  #ifdef __NT__    string binary = "mysqldump.exe";   #else    string binary = "mysqldump";   #endif    res->mysqldump =    check_paths( ({ combine_path(res->basedir, "libexec", binary),    combine_path(res->basedir, "bin", binary),    combine_path(res->basedir, "sbin", binary) }) );    } +  +  // Locate mysql_upgrade +  if (!res->mysql_upgrade) { + #ifdef __NT__ +  string binary = "mysql_upgrade.exe"; + #else +  string binary = "mysql_upgrade"; + #endif +  res->mysql_upgrade = +  check_paths( ({ combine_path(res->basedir, "libexec", binary), +  combine_path(res->basedir, "bin", binary), +  combine_path(res->basedir, "sbin", binary) }) );    } -  +  }       return cached_mysql_location = res;   }      mapping(string:string) parse_mysql_location()   // Compatibility alias.   {    return mysql_location();   }   
Roxen.git/server/base_server/roxenloader.pike:1768:    new_dir = combine_path(getcwd(), datadir, "mysql");    if(new_dir && Stdio.exist(new_dir))    return new_dir;    if(Stdio.exist(old_dir))    return old_dir;    if(new_dir)    return new_dir;    return old_dir;   }    + string query_mysql_socket() + { + #ifdef __NT__ +  return replace(combine_path(query_mysql_data_dir(), "pipe"), ":", "_"); + #else +  return combine_path(query_mysql_data_dir(), "socket"); + #endif + } +    string my_mysql_path;      string query_configuration_dir()   {    return configuration_dir;   }      protected mapping(string:array(SQLTimeout)) sql_free_list = ([ ]);   protected Thread.Local sql_reuse_in_thread = Thread.Local();   mapping(string:int) sql_active_list = ([ ]);
Roxen.git/server/base_server/roxenloader.pike:1877:    return real->fetch_fields();    }    protected void seek(int skip)    {    real->seek(skip);    }    protected int|array(string|int) fetch_row()    {    return real->fetch_row();    } -  static int|string fetch_json_result() +  protected int|string fetch_json_result()    {    return real->fetch_json_result();    }       protected int(0..1) `!()    {    return !real;    }       // Iterator copied from Sql.sql_result. It's less hassle to
Roxen.git/server/base_server/roxenloader.pike:2133:    case "db_name": return db_name;    case "reuse_in_thread": return reuse_in_thread;    case "query": return query;    case "big_query": return big_query;    }    return real[what];    }       protected string _sprintf(int type)    { -  return sprintf( "SQLKey(%O, %O)" + OBJ_COUNT, db_name, real ); +  string display_name = db_name || ""; +  if (has_suffix(display_name, ":-")) { +  // Unmangle the mangling from DBManager.sql_cache_get(). +  display_name = replace(display_name[..<2], ";", ":");    } -  +  array(string) a = display_name/"://"; +  string prot = a[0]; +  string host = a[1..] * "://"; +  a = host/"@"; +  if (sizeof(a) > 1) { +  host = a[-1]; +  a = (a[..<1] * "@")/":"; +  string user = a[0]; +  if (sizeof(a) > 1) { +  display_name = prot + "://" + user + ":CENSORED@" + host;    } -  +  } +  return sprintf( "SQLKey(%O, %O)" + OBJ_COUNT, display_name, real ); +  } + }      protected Thread.Mutex mt = Thread.Mutex();   Thread.MutexKey sq_cache_lock()   {    return mt->lock();   }      protected mapping(program:string) default_db_charsets = ([]);    -  + //! Get a cached connection to an SQL database. + //! + //! @param db_name + //! SQL-URL for the connection. + //! + //! @param reuse_in_thread + //! Use a thread-dedicated cache.   Sql.Sql sq_cache_get( string db_name, void|int reuse_in_thread)   {    Sql.Sql db; -  +  Thread.MutexKey key = sq_cache_lock();       if (reuse_in_thread) {    mapping(string:Sql.Sql) dbs_for_thread = sql_reuse_in_thread->get();    db = dbs_for_thread && dbs_for_thread[db_name];    }       else { -  while(sql_free_list[ db_name ]) +  while(sizeof(sql_free_list[db_name] || ({})))    {   #ifdef DB_DEBUG    werror("%O found in free list\n", db_name );   #endif    SQLTimeout res = sql_free_list[db_name][0];    if( sizeof( sql_free_list[ db_name ] ) > 1)    sql_free_list[ db_name ] = sql_free_list[db_name][1..];    else    m_delete( sql_free_list, db_name ); -  if ((db = res && res->get()) && db->is_open()) { +  if (res) { +  destruct(key); +  // NB: Release the lock during connection validation. Cf [WS-28]. +  if ((db = res->get()) && db->is_open()) { +  key = sq_cache_lock();    sql_active_list[db_name]++;    break;    } -  +  key = sq_cache_lock();    }    } -  +  }       if (db)    return [object(Sql.Sql)] (object) SQLKey (db, db_name, reuse_in_thread);    return 0;   }      Sql.Sql fix_connection_charset (Sql.Sql db, string charset)   {    if (object master_sql = db->master_sql) {    if (mixed err = catch {
Roxen.git/server/base_server/roxenloader.pike:2240:    } while (0)      Sql.Sql sq_cache_set( string db_name, Sql.Sql res,    void|int reuse_in_thread, void|string charset)   // Should only be called with a "virgin" Sql.Sql object that has never   // been used or had its charset changed.   {    if( res )    {    FIX_CHARSET_FOR_NEW_SQL_CONN (res, charset); +  Thread.MutexKey key = sq_cache_lock();    sql_active_list[ db_name ]++;    return [object(Sql.Sql)] (object) SQLKey( res, db_name, reuse_in_thread);    }   }      /* Not to be documented. This is a low-level function that should be    * avoided by normal users.   */   Sql.Sql connect_to_my_mysql( string|int ro, void|string db,    void|int reuse_in_thread, void|string charset)   {   #if 0   #ifdef DB_DEBUG    gc();   #endif   #endif -  Thread.MutexKey key; +  string i = db+":"+(intp(ro)?(ro&&"ro")||"rw":ro); +  Sql.Sql res;    if (catch { -  key = sq_cache_lock(); +  res = sq_cache_get(i, reuse_in_thread);    }) {    // Threads disabled.    // This can occur if we are called from the compiler. -  +  // NB: This is probably dead code with Pike 8.0 and later, +  // as the compiler no longer disables all threads.    Sql.Sql res = low_connect_to_my_mysql(ro, db);    FIX_CHARSET_FOR_NEW_SQL_CONN (res, charset);    return res;    } -  string i = db+":"+(intp(ro)?(ro&&"ro")||"rw":ro); -  Sql.Sql res = sq_cache_get(i, reuse_in_thread); +     if (res) { -  destruct (key); +     return fix_connection_charset (res, charset);    } -  destruct(key); +     if (res = low_connect_to_my_mysql( ro, db )) { -  key = sq_cache_lock(); -  // Fool the optimizer so that key is not released prematurely -  if( res ) +     return sq_cache_set(i, res, reuse_in_thread, charset);    }    return 0;   }      protected mixed low_connect_to_my_mysql( string|int ro, void|string db )   {    object res;   #ifdef DB_DEBUG    werror("Requested %O for %O DB\n", db, ro );
Roxen.git/server/base_server/roxenloader.pike:2428:    ]))->wait();    };    if(err)    werror(describe_backtrace(err));   }      void low_start_mysql( string datadir,    string uid,    void|int log_queries_to_stdout)   { -  array MYSQL_GOOD_VERSION = ({ "5.0.*", - #ifdef YES_I_KNOW_WHAT_I_AM_DOING -  "*" - #endif -  }); -  array MYSQL_MAYBE_VERSION = ({ "5.1.*", "5.5.*", "6.*" }); -  +     void rotate_log(string path)    {    rm(path+".5");    for(int i=4; i>0; i--)    mv(path+"."+(string)i, path+"."+(string)(i+1));    };       // Get mysql base directory and binary paths    mapping mysql_location = this_program::mysql_location();    if (!mysql_location->mysqld) {    report_debug("\nNo MySQL found in "+ mysql_location->basedir + "!\n");    exit(1);    }       // Start by verifying the mysqld version    string version_fatal_error = 0; -  string version = popen(({mysql_location->mysqld, "--version"})); +  string version = popen(({ mysql_location->mysqld, +  "--version", "--no-defaults", +  }));    if (!version) {    version_fatal_error =    sprintf("Unable to determine MySQL version with this command:\n\n" -  " %s --version\n\n", +  " %s --version --no-defaults\n\n",    mysql_location->mysqld);    } else {    // Parse version string    string orig_version = version; -  +  string trailer;    if (has_prefix (version, mysql_location->mysqld))    // mysqld puts $0 first in the version string. Cut it off to    // avoid possible false matches.    version = version[sizeof (mysql_location->mysqld)..]; -  if (sscanf(lower_case(version), "%*s ver %[0-9.]", version) != 2) { +  if (sscanf(lower_case(version), "%*s ver %[0-9.]%s", +  mysql_version, trailer) < 2) {    version_fatal_error =    sprintf("Failed to parse MySQL version string - got %q from:\n"    "%O\n\n", version, orig_version); -  + #ifndef YES_I_KNOW_WHAT_I_AM_DOING    } else { -  +  array(string) good_versions = mysql_good_versions; +  array(string) maybe_versions = mysql_maybe_versions; +  mysql_product_name = "MySQL"; +  if (has_prefix(trailer, "-mariadb")) { +  mysql_product_name = "MariaDB"; +  good_versions = mariadb_good_versions; +  maybe_versions = mariadb_maybe_versions; +  }    // Determine if version is acceptable -  if (has_value(glob(MYSQL_GOOD_VERSION[*], version), 1)) { +  if (has_value(glob(good_versions[*], mysql_version), 1)) {    // Everything is fine -  } else if (has_value(glob(MYSQL_MAYBE_VERSION[*], version), 1)) { +  } else if (has_value(glob(maybe_versions[*], mysql_version), 1)) {    // Don't allow unless user gives special define   #ifdef ALLOW_UNSUPPORTED_MYSQL    report_debug("\nWARNING: Forcing Roxen to run with unsupported " -  "MySQL version (%s).\n", -  version); +  "%s version (%s).\n", +  mysql_product_name, mysql_version);   #else    version_fatal_error = -  sprintf("This version of MySQL (%s) is not officially supported " +  sprintf("This version of %s (%s) is not officially supported "    "with Roxen.\n"    "If you want to override this restriction, use this "    "option:\n\n"    " -DALLOW_UNSUPPORTED_MYSQL\n\n", -  version); +  mysql_product_name, mysql_version);   #endif    } else {    // Version not recognized (maybe too old or too new) so bail out    version_fatal_error = -  sprintf("MySQL version %s detected:\n\n" -  " %s\n", version, orig_version); +  sprintf("%s version %s detected:\n\n" +  " %s\n", mysql_product_name, mysql_version, orig_version);    } -  + #endif   #ifdef RUN_SELF_TEST    if (version_fatal_error) {    report_debug ("\n%s"    "Continuing anyway in self test mode.\n\n",    version_fatal_error);    version_fatal_error = 0;    }   #endif    }    }
Roxen.git/server/base_server/roxenloader.pike:2536:    array(string) args = ({    "--defaults-file="+datadir+"/my.cfg",   #ifdef __NT__    // Use pipes with default name "MySQL" unless --socket is set    "--socket="+replace(datadir, ":", "_") + "/pipe",    "--enable-named-pipe",   #else    "--socket="+datadir+"/socket",    "--pid-file="+pid_file,   #endif -  "--skip-locking", +  has_prefix(version, "5.1.")? +  "--skip-locking":"--skip-external-locking",    "--skip-name-resolve",    "--basedir=" + mysql_location->basedir,    "--datadir="+datadir,    });       // Set up the environment variables, and    // enable mysql networking if necessary.    mapping env = getenv();    env->MYSQL_UNIX_PORT = datadir+"/socket";    if ((int)env->ROXEN_MYSQL_TCP_PORT) {
Roxen.git/server/base_server/roxenloader.pike:2558:    args += ({ "--port="+env->MYSQL_TCP_PORT });    if (!env->MYSQL_HOST) {    env->MYSQL_HOST = "127.0.0.1";    }    } else {    args += ({ "--skip-networking" });    env->MYSQL_HOST = "127.0.0.1";    env->MYSQL_TCP_PORT = "0";    }    +  string normalized_mysql_version = +  map(mysql_version/".", +  lambda(string d) { +  return ("000" + d)[<2..]; +  }) * "."; +     if(!env->ROXEN_MYSQL_SLOW_QUERY_LOG ||    env->ROXEN_MYSQL_SLOW_QUERY_LOG != "0") {    rotate_log(slow_query_log); -  +  if (normalized_mysql_version > "005.006.") { +  args += ({ +  "--slow-query-log-file="+slow_query_log+".1", +  "--slow-query-log", +  }); +  } else { +  // NB: Deprecated in MySQL 5.1.29 and removed in MySQL 5.6.1.    args += ({ "--log-slow-queries="+slow_query_log+".1" }); -  +  }    report_debug("Setting MySQL's slow query log to \"%s.1\"\n", slow_query_log);    }    -  if (log_queries_to_stdout) +  if (log_queries_to_stdout) { +  if (normalized_mysql_version > "005.006.") { +  args += ({ +  "--general-log-file=/dev/stdout", +  "--general-log", +  }); +  } else { +  // NB: Deprecated in MySQL 5.1.29 and removed in MySQL 5.6.1.    args += ({"--log=/dev/stdout"}); -  +  } +  }       // Create the configuration file. -  string cfg_file = ("[mysqld]\n" -  "set-variable = max_allowed_packet=16M\n" -  "set-variable = net_buffer_length=8K\n" +  int force = !file_stat( datadir+"/my.cfg" ); +  string cfg_file = (Stdio.read_bytes(datadir + "/my.cfg") || +  "[mysqld]\n" +  "max_allowed_packet = 128M\n" +  "net_buffer_length = 8K\n"    "query-cache-type = 2\n"    "query-cache-size = 32M\n" -  +  "default-storage-engine = MYISAM\n" +  "innodb-data-file-path=ibdata1:10M:autoextend\n"   #ifndef UNSAFE_MYSQL    "local-infile = 0\n"   #endif    "skip-name-resolve\n" -  +  "character-set-server=latin1\n" +  "collation-server=latin1_swedish_ci\n"    "bind-address = "+env->MYSQL_HOST+"\n" +    (uid ? "user = " + uid : "") + "\n");    -  +  string normalized_cfg_file = replace(cfg_file, "_", "-"); +  +  // Check if we need to update the contents of the config file. +  // +  // NB: set-variable became optional after MySQL 4.0.2, +  // and was deprecated in MySQL 5.5. +  if (has_value(normalized_cfg_file, "set-variable=") || +  has_value(normalized_cfg_file, "set-variable =")) { +  report_debug("Repairing pre Mysql 4.0.2 syntax in %s/my.cfg.\n", datadir); +  cfg_file = replace(cfg_file, +  ({ "set-variable=", +  "set-variable = ", "set-variable =", +  "set_variable=", +  "set_variable = ", "set_variable =", +  }), +  ({ "", "", "", "", "", "", +  })); +  force = 1; +  } +  +  if ((normalized_mysql_version > "005.000.") && +  !has_value(normalized_cfg_file, "innodb-data-file-path")) { +  // It seems the defaults for this variable have changed +  // from "ibdata1:10M:autoextend" to "ibdata1:12M:autoextend". +  // For some reason InnoDB doesn't always auto-detect correctly. +  // cf [bug 7264]. +  array a = cfg_file/"[mysqld]"; +  if (sizeof(a) > 1) { +  report_debug("Adding innodb-data-file-path to %s/my.cfg.\n", +  datadir); +  int initial = 10; // 10 MB -- The traditional setting. +  int bytes = Stdio.file_size(datadir + "/ibdata1"); +  if (bytes) { +  // ibdata1 grows in increments of 8 MB. +  // Assumes that the initial default size won't grow to 18 MB. +  initial = ((bytes / (1024 * 1024)) % 8) + 8; +  if (initial < 10) initial += 8; +  } +  report_debug("%O\n", +  "ibdata1:" + initial + "M:autoextend"); +  a[1] = "\n" +  "innodb-data-file-path=ibdata1:" + initial + "M:autoextend" + a[1]; +  cfg_file = a * "[mysqld]"; +  force = 1; +  } else { +  report_warning("Mysql configuration file %s/my.cfg lacks\n" +  "InnoDB data file path entry, " +  "and automatic repairer failed.\n", +  datadir); +  } +  } +  +  if ((normalized_mysql_version > "005.002.") && +  !has_value(normalized_cfg_file, "character-set-server")) { +  // The default character set was changed sometime +  // during the MySQL 5.x series. We need to set +  // the default to latin1 to avoid breaking old +  // internal tables (like eg roxen/dbs) where fields +  // otherwise shrink to a third. +  array a = cfg_file/"[mysqld]"; +  if (sizeof(a) > 1) { +  report_debug("Adding default character set entries to %s/my.cfg.\n", +  datadir); +  a[1] = "\n" +  "character-set-server=latin1\n" +  "collation-server=latin1_swedish_ci" + a[1]; +  cfg_file = a * "[mysqld]"; +  force = 1; +  } else { +  report_warning("Mysql configuration file %s/my.cfg lacks\n" +  "character set entry, and automatic repairer failed.\n", +  datadir); +  } +  } +  +  if ((normalized_mysql_version > "005.005.") && +  !has_value(normalized_cfg_file, "default-storage-engine")) { +  // The default storage engine was changed to InnoDB in MySQL 5.5. +  // We need to set the default to MyISAM to avoid breaking old code +  // due to different parameter limits (eg key lengths). +  array a = cfg_file/"[mysqld]"; +  if (sizeof(a) > 1) { +  report_debug("Adding default storage engine entry to %s/my.cfg.\n", +  datadir); +  a[1] = "\n" +  "default-storage-engine = MYISAM" + a[1]; +  cfg_file = a * "[mysqld]"; +  force = 1; +  } else { +  report_warning("Mysql configuration file %s/my.cfg lacks\n" +  "storage engine entry, and automatic repairer failed.\n", +  datadir); +  } +  } +  +  if ((normalized_mysql_version > "010.002.003") && +  !has_value(normalized_cfg_file, "sql_mode")) { +  // Since MariaDB 10.2.4, SQL_MODE is by default set to NO_AUTO_CREATE_USER, +  // NO_ENGINE_SUBSTITUTION, STRICT_TRANS_TABLES, ERROR_FOR_DIVISION_BY_ZERO. +  // In earlier versions of MariaDB 10.2, and since MariaDB 10.1.7, SQL_MODE +  // is by default set to NO_ENGINE_SUBSTITUTION, NO_AUTO_CREATE_USER. +  // For earlier versions of MariaDB 10.1, and MariaDB 10.0 and before, no +  // default is set. +  // +  // This change in 10.2 can cause queries to fail, complaining about +  // no default values: +  // +  // big_query(): Query failed (Field 'x' doesn't have a default value) +  // +  // cf: +  // https://www.slickdev.com/2017/09/05/mariadb-10-2-field-xxxxxxx-doesnt-default-value-error/ +  array a = cfg_file/"[mysqld]"; +  if (sizeof(a) > 1) { +  report_debug("Adding sql_mode entry to %s/my.cfg.\n", datadir); +  a[1] = "\n" +  "sql_mode = NO_ENGINE_SUBSTITUTION" + a[1]; +  cfg_file = a * "[mysqld]"; +  force = 1; +  } else { +  report_warning("Mysql configuration file %s/my.cfg lacks\n" +  "sql_mode entry, and automatic repairer failed.\n", +  datadir); +  } +  } +    #ifdef __NT__ -  cfg_file = replace(cfg_file, "\n", "\r\n"); +  cfg_file = replace(cfg_file, ({ "\r\n", "\n" }), ({ "\r\n", "\r\n" }));   #endif /* __NT__ */    -  if(!file_stat( datadir+"/my.cfg" )) +  if(force)    catch(Stdio.write_file(datadir+"/my.cfg", cfg_file));       // Keep mysql's logging to stdout and stderr when running in --once    // mode, to get it more synchronous.    Stdio.File errlog = !once_mode && Stdio.File( err_log, "wct" );       string mysql_table_check =    Stdio.read_file(combine_path(query_configuration_dir(),    "_mysql_table_check"));    if(!mysql_table_check)
Roxen.git/server/base_server/roxenloader.pike:2680:    // At this moment new_master does not exist, and    // DBManager can not possibly compile. :-)    call_out( lambda(){    new_master->resolv("DBManager.is_module_table")    ( 0, "local", "precompiled_files",    "Contains binary object code for .pike files. "    "This information is used to shorten the "    "boot time of Roxen by keeping the compiled "    "data instead of recompiling it every time.");    }, 1 ); -  +     } -  +  +  // At this moment new_master does not exist, and +  // DBManager can not possibly compile. :-) +  call_out( lambda(){ +  // Inhibit backups of the precompiled_files table. +  new_master->resolv("DBManager.inhibit_backups") +  ("local", "precompiled_files"); +  }, 1 ); +     if( remove_dumped )    {    report_notice("Removing precompiled files\n");    if (mixed err = catch    {    db->query( "DELETE FROM local.precompiled_files" );    db->query( "DELETE FROM local.compiled_formats" );    // Clear the modules cache too since it currently doesn't    // depend on the module path properly.    db->query( "DELETE FROM local.modules" );
Roxen.git/server/base_server/roxenloader.pike:2721:    // UTF8 and explicit character set markup was added in Mysql 4.1.x.    add_constant("ROXEN_MYSQL_SUPPORTS_UNICODE", 1);    }       if( !do_tailf_threaded && !once_mode ) do_tailf(0, err_log );    assure_that_base_tables_exists();    };       void start_tailf()    { - #if constant (thread_create) +     if( do_tailf_threaded ) {    thread_create( do_tailf, 1, err_log );    sleep(0.1); -  } -  else - #endif -  { +  } else {    do_tailf(0, err_log );    void do_do_tailf( )    {    call_out( do_do_tailf, 1 );    do_tailf( 0, err_log );    };    call_out( do_do_tailf, 0 );    }    };   
Roxen.git/server/base_server/roxenloader.pike:2972: Inside #if defined(GC_TRACE)
     #ifdef GC_TRACE    trace (GC_TRACE, "gc");   #endif       nwrite = early_nwrite;       add_constant( "connect_to_my_mysql", connect_to_my_mysql );    add_constant( "clear_connect_to_my_mysql_cache",    clear_connect_to_my_mysql_cache ); +  + #if !constant(thread_create) +  report_debug(#" +  +  + ------ FATAL ---------------------------------------------------- + Roxen requires Pike with thread support. + ----------------------------------------------------------------- +  +  + "); +  exit(-1); + #endif +    #ifdef SECURITY   #if !constant(__builtin.security.Creds) -  report_debug( - #" +  report_debug(#" +  +    ------ FATAL ----------------------------------------------------   SECURITY defined (the internal security system in roxen), but   the pike binary has not been compiled --with-security. This makes   it impossible for roxen to have any internal security at all.   ----------------------------------------------------------------- -  +  +    ");    exit(-1);   #endif   #endif       if( (-1&0xffffffff) < 0 )    { -  report_debug( - #" +  report_debug(#" +  +    ------- WARNING -----------------------------------------------   Roxen requires bignum support in Pike since version 2.4.   Please recompile Pike with gmp / bignum support to run Roxen.      It might still be possible to start Roxen, but the   functionality will be affected, and stange errors might occur.   ---------------------------------------------------------------    -  +    ");    }      #ifdef NOT_INSTALLED -  report_debug( - #" +  report_debug(#" +  +    ------- WARNING -----------------------------------------------   You are running with an un-installed Pike binary.      Please note that this is unsupported, and might stop working at   any time, since some things are done differently in uninstalled   Pikes, as an example the module search paths are different, and   some environment variables are ignored.   ---------------------------------------------------------------    -  +    ");   #endif    - #if __VERSION__ < 7.8 -  report_debug( - #" + #if __VERSION__ < 8.0 +  report_debug(#"         ****************************************************** - Roxen 5.0 requires Pike 7.8 or newer. + Roxen " + roxen_ver + #" requires Pike 8.0 or newer.   Please install a newer version of Pike.   ******************************************************         ");    _exit(0); /* 0 means stop start script looping */ - #endif /* __VERSION__ < 7.8 */ + #endif /* __VERSION__ < 8.0 */      #if !constant (Mysql.mysql)    report_debug (#"         ******************************************************   Roxen requires MySQL support in Pike since version 2.4.   Your Pike has been compiled without support for MySQL.   Please install MySQL client libraries and reconfigure   and rebuild Pike from source.   ******************************************************         ");    _exit(0); // 0 means stop start script looping   #endif // !constant (Mysql.mysql)    -  + #if !constant (Regexp.PCRE) +  report_debug (#" +  +  + ****************************************** + Roxen requires Regexp.PCRE support in Pike + ****************************************** +  +  + "); +  _exit(0); // 0 means stop start script looping + #endif // !constant (Regexp.PCRE) +  +  +  string s; +  if (!catch(s = _Roxen->make_http_headers((["a\r\n":"b\r\n"]), 1)) && +  (sizeof(s/"\r\n") > 2)) { +  add_constant("HAVE_OLD__Roxen_make_http_headers", 1); +  report_debug(#" +  +  + ------- WARNING ----------------------------------------------- + Old or broken _Roxen.make_http_headers() detected. +  + Roxen 6.0 prefers Pike 8.0.270 or later. + Roxen will still work, but at lower performance. + Please install a newer version of Pike. + --------------------------------------------------------------- +  +  + "); +  } +     Stdio.Stat stat = file_stat("etc/include/version.h");    if (stat && (stat->mtime > time())) {    report_debug(#"         ------- WARNING -----------------------------------------------   System time is incorrect.      System time: %s   Check time: %s
Roxen.git/server/base_server/roxenloader.pike:3113:    mixed err;       add_constant("open", open);    add_constant("roxen_path", roxen_path);    add_constant("roxen_version", roxen_version);    add_constant("roxen_dist_version", dist_version);    add_constant("roxen_dist_os", dist_os);    add_constant("roxen_release", release || roxen_release);    add_constant("roxen_is_cms", roxen_is_cms);    add_constant("roxen_product_name", roxen_product_name); +  add_constant("roxen_product_code", roxen_product_code);    add_constant("lopen", lopen);    add_constant("lfile_stat", lfile_stat);    add_constant("lfile_path", lfile_path);    add_constant("report_notice", report_notice);    add_constant("report_debug", report_debug);    add_constant("report_warning",report_warning);    add_constant("report_error", report_error);    add_constant("report_fatal", report_fatal);    add_constant("report_notice_for", report_notice_for);    add_constant("report_warning_for", report_warning_for);
Roxen.git/server/base_server/roxenloader.pike:3159:    add_constant("r_get_dir", r_get_dir);    add_constant("r_file_stat", file_stat);    add_constant("r_is_file", r_is_file);    add_constant("r_is_dir", r_is_dir);    add_constant("r_is_link", r_is_link);    add_constant("r_exist", r_exist);    add_constant("r_read_bytes", r_read_bytes);    add_constant("roxenloader", this_object());    add_constant("ErrorContainer", ErrorContainer);    + #ifdef THREADS +  add_constant("euid_egid_lock", euid_egid_lock); + #endif + #ifndef __NT__ +  if(!getuid()) +  add_constant("Privs", Privs); +  else + #endif /* !__NT__ */ +  add_constant("Privs", class { +  void create(string reason, int|string|void uid, int|string|void gid) {} +  }); +     add_constant("_cur_rxml_context", Thread.Local());    -  +  int mysql_only_mode = +  (int)Getopt.find_option(hider, "mysql-only", ({ "mysql-only" })); +  if (mysql_only_mode) { +  // Force --once mode. +  // +  // This avoids starting eg the tailf thread. +  once_mode = 1; +  } +     if (has_value (hider, "--mysql-log-queries")) {    hider -= ({"--mysql-log-queries"});    argc = sizeof (hider);    start_mysql (1);    }    else    start_mysql (0);    -  +  if (mysql_only_mode) { +  exit(0); +  } +     if (err = catch {    if(master()->relocate_module) add_constant("PIKE_MODULE_RELOC", 1);    replace_master(new_master=[object(__builtin.__master)](((program)"etc/roxen_master.pike")()));    }) {    werror("Initialization of Roxen's master failed:\n"    "%s\n", describe_backtrace(err));    exit(1);    }       // Restore describe_backtrace(), which was zapped by the new master.    add_constant ("describe_backtrace", describe_backtrace);      #if constant( Gz.inflate )    add_constant("grbz",lambda(string d){return Gz.inflate()->inflate(d);});   #else    add_constant("grbz",lambda(string d){return d;}); -  report_debug( - #" +  report_debug(#" +  +    ------- WARNING -----------------------------------------   The Gz (zlib) module is not available.   The default builtin font will not be available.   To get zlib support, install zlib from   ftp://ftp.freesoftware.com/pub/infozip/zlib/zlib.html   and recompile pike, after removing the file 'config.cache'   ----------------------------------------------------------    -  +    ");   #endif       add_constant("spawne",spawne);    add_constant("spawn_pike",spawn_pike);    add_constant("popen",popen);    add_constant("roxen_popen",popen);    add_constant("init_logger", init_logger);    add_constant("capitalize", String.capitalize);   
Roxen.git/server/base_server/roxenloader.pike:3221: Inside #if constant (Image.TTF)
   add_constant( "Image.TTF", Image.TTF );    // We can load the builtin font.    add_constant("__rbf", "font_handlers/rbf" );    } else   #endif    {   #if constant(Image.FreeType.Face)    // We can load the builtin font.    add_constant("__rbf", "font_handlers/rbf" );   #else -  report_debug( - #" +  report_debug(#" +  +    ------- WARNING ----------------------------------------------   Neither the Image.TTF nor the Image.FreeType module is available.   True Type fonts and the default font will not be available.   To get True Type support, download a Freetype package from      http://freetype.sourceforge.net/download.html      Install it, and then remove config.cache in pike and recompile.   If this was a binary release of Roxen, there should be no need   to recompile the pike binary, since the one included should   already have the FreeType interface module, installing the   library should be enough.   --------------------------------------------------------------    -  +    " );   #endif    }       if( search( hider, "--long-error-file-names" ) != -1 )    {    hider -= ({ "--long-error-file-names" });    argc = sizeof(hider); -  new_master->long_file_names = 1; +     new_master->putenv("LONG_PIKE_ERRORS", "yup");    }    -  +  array(string) patches = get_dir("patches"); +  if (patches && sizeof(patches)) { +  report_debug("Installed patches:\n"); +  foreach(sort(patches), string patch) { +  report_debug(" %s\n", patch); +  } +  report_debug("\n"); +  } +     // These are here to allow dumping of roxen.pike to a .o file.    report_debug("Loading Pike modules ... \b");       add_dump_constant = new_master->add_dump_constant;    int t = gethrtime();       DC("Thread.Thread"); DC("Thread.Local");    DC("Thread.Mutex"); DC("Thread.MutexKey");    DC("Thread.Condition"); DC("thread_create");    DC( "Thread.Queue" );
Roxen.git/server/base_server/roxenloader.pike:3310:    DC( "Stdio.read_bytes" ); DC( "Stdio.read_file" );    DC( "Stdio.write_file" );          DC( "Stdio.sendfile" );       DC( "Stdio.stderr" ); DC( "Stdio.stdin" ); DC( "Stdio.stdout" );       DC( "Parser.HTML" );    -  if( DC("SSL.sslfile" ) ) +  if( DC("SSL.File" ) )    {    DC( "SSL.context" );    DC( "Tools.PEM.pem_msg" );    DC( "Crypto.Random.random_string" );    DC( "Standards.PKCS.RSA.parse_private_key");    DC( "Crypto.RSA" );    DC( "Tools.X509.decode_certificate" );    DC( "Standards.PKCS.DSA.parse_private_key" );    DC( "SSL.cipher.dh_parameters" );    }
Roxen.git/server/base_server/roxenloader.pike:3332:    if( DC( "HTTPLoop.prog" ) )    DC( "HTTPLoop.Loop" );       if( DC( "Image.FreeType" ) )    DC( "Image.FreeType.Face" );       DC( "Process.create_process" );    DC( "MIME.Message" ); DC( "MIME.encode_base64" );    DC( "MIME.decode_base64" );    -  DC( "Locale" ); DC( "Locale.Charset" ); +  DC( "Locale" );    -  +  DC( "Charset" ); +     report_debug("\bDone [%.1fms]\n", (gethrtime()-t)/1000.0);       add_constant( "hsv_to_rgb", nm_resolv("Colors.hsv_to_rgb") );    add_constant( "rgb_to_hsv", nm_resolv("Colors.rgb_to_hsv") );    add_constant( "parse_color", nm_resolv("Colors.parse_color") );    add_constant( "color_name", nm_resolv("Colors.color_name") );    add_constant( "colors", nm_resolv("Colors") );    -  +  add_constant("verify_password", verify_password); +  add_constant("crypt_password", crypt_password); +     // report_debug("Loading prototypes ... \b");    // t = gethrtime();       // Load prototypes (after the master is replaces, thus making it    // possible to dump them to a .o file (in the mysql))    object prototypes = (object)"base_server/prototypes.pike";    dump( "base_server/prototypes.pike", object_program( prototypes ) );    foreach (indices (prototypes), string id)    if (!prototypes->ignore_identifiers[id])    add_constant (id, prototypes[id]);