text: /** @file
  
    A brief file description
  
    @section license License
  
    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.
   */
  
  /***************************************************************************
   LogStandalone.cc
  
  
   ***************************************************************************/
  
  #include "ts/ink_platform.h"
  #include "ts/ink_lockfile.h"
  #include "ts/ink_sys_control.h"
  #include "ts/signals.h"
  #include "DiagsConfig.h"
  #include "Main.h"
  
  #include "P_EventSystem.h"
  #include "P_RecProcess.h"
  
  #include "ProcessManager.h"
  #include "MgmtUtils.h"
  // Needs LibRecordsConfigInit()
  #include "RecordsConfig.h"
  
  #define LOG_FILENAME_SIZE 255
  
  class HttpBodyFactory;
  
  // globals the rest of the system depends on
  extern int fds_limit;
  extern int cluster_port_number;
  
  int command_flag                = 0;
  int http_accept_port_number     = 0;
  int http_accept_file_descriptor = 0;
  int remote_management_flag      = 0;
  int auto_clear_hostdb_flag      = 0;
  char proxy_name[MAXDNAME + 1]   = "unknown";
  
  char error_tags[1024]    = "";
  char action_tags[1024]   = "";
  char command_string[512] = "";
  
  // Diags *diags = NULL;
  DiagsConfig *diagsConfig      = nullptr;
  HttpBodyFactory *body_factory = nullptr;
  AppVersionInfo appVersionInfo;
  
  /*-------------------------------------------------------------------------
    init_system
    -------------------------------------------------------------------------*/
  
  // Handle fatal signals by logging and core dumping ...
  static void
  logging_crash_handler(int signo, siginfo_t *info, void *ptr)
  {
    signal_format_siginfo(signo, info, appVersionInfo.AppStr);
    signal_crash_handler(signo, info, ptr);
  }
  
  static void
  init_system(bool notify_syslog)
  {
    fds_limit = ink_max_out_rlimit(RLIMIT_NOFILE, true, false);
  
    signal_register_crash_handler(logging_crash_handler);
    if (notify_syslog) {
      syslog(LOG_NOTICE, "NOTE: --- %s Starting ---", appVersionInfo.AppStr);
      syslog(LOG_NOTICE, "NOTE: %s Version: %s", appVersionInfo.AppStr, appVersionInfo.FullVersionInfoStr);
    }
  }
  
  /*-------------------------------------------------------------------------
    initialize_process_manager
    -------------------------------------------------------------------------*/
  
  static void
  initialize_process_manager()
  {
    mgmt_use_syslog();
  
    // Temporary Hack to Enable Communuication with LocalManager
    if (getenv("PROXY_REMOTE_MGMT")) {
      remote_management_flag = true;
    }
  
    // diags should have been initialized by caller, e.g.: sac.cc
    ink_assert(diags);
  
    RecProcessInit(remote_management_flag ? RECM_CLIENT : RECM_STAND_ALONE, diags);
    LibRecordsConfigInit();
  
    // Start up manager
    pmgmt = new ProcessManager(remote_management_flag);
  
    pmgmt->start();
  
    RecProcessInitMessage(remote_management_flag ? RECM_CLIENT : RECM_STAND_ALONE);
  
    pmgmt->reconfigure();
  
    //
    // Define version info records
    //
    RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.short", appVersionInfo.VersionStr, RECP_NON_PERSISTENT);
    RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.long", appVersionInfo.FullVersionInfoStr, RECP_NON_PERSISTENT);
    RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.build_number", appVersionInfo.BldNumStr, RECP_NON_PERSISTENT);
    RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.build_time", appVersionInfo.BldTimeStr, RECP_NON_PERSISTENT);
    RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.build_date", appVersionInfo.BldDateStr, RECP_NON_PERSISTENT);
    RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.build_machine", appVersionInfo.BldMachineStr,
                          RECP_NON_PERSISTENT);
    RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.build_person", appVersionInfo.BldPersonStr,
                          RECP_NON_PERSISTENT);
    //    RecRegisterStatString(RECT_PROCESS,
    //                         "proxy.process.version.server.build_compile_flags",
    //                         appVersionInfo.BldCompileFlagsStr,
    //                         RECP_NON_PERSISTENT);
  }
  
  /*-------------------------------------------------------------------------
    check_lockfile
    -------------------------------------------------------------------------*/
  
  static void
  check_lockfile()
  {
    int err;
    pid_t holding_pid;
    char *lockfile = nullptr;
  
    if (access(Layout::get()->runtimedir.c_str(), R_OK | W_OK) == -1) {
      fprintf(stderr, "unable to access() dir'%s': %d, %s\n", Layout::get()->runtimedir.c_str(), errno, strerror(errno));
      fprintf(stderr, " please set correct path in env variable TS_ROOT \n");
      ::exit(1);
    }
    lockfile = ats_stringdup(Layout::relative_to(Layout::get()->runtimedir, SERVER_LOCK));
  
    Lockfile server_lockfile(lockfile);
    err = server_lockfile.Get(&holding_pid);
  
    if (err != 1) {
      char *reason = strerror(-err);
      fprintf(stderr, "FATAL: Can't acquire lockfile '%s'", lockfile);
  
      if ((err == 0) && (holding_pid != -1)) {
  #if defined(solaris)
        fprintf(stderr, " (Lock file held by process ID %d)\n", (int)holding_pid);
  #else
        fprintf(stderr, " (Lock file held by process ID %d)\n", holding_pid);
  #endif
      } else if ((err == 0) && (holding_pid == -1)) {
        fprintf(stderr, " (Lock file exists, but can't read process ID)\n");
      } else if (reason) {
        fprintf(stderr, " (%s)\n", reason);
      } else {
        fprintf(stderr, "\n");
      }
      ::exit(1);
    }
    ats_free(lockfile);
  }
  
  /*-------------------------------------------------------------------------
    init_log_standalone
  
    This routine should be called from the main() function of the standalone
    program.
    -------------------------------------------------------------------------*/
  
  void
  init_log_standalone(const char *pgm_name, bool one_copy)
  {
    char logfile[LOG_FILENAME_SIZE];
  
    // ensure that only one copy of the sac is running
    //
    if (one_copy) {
      check_lockfile();
    }
  
    snprintf(logfile, sizeof(logfile), "%s.log", pgm_name);
  
    // set stdin/stdout to be unbuffered
    //
    setbuf(stdin, nullptr);
    setbuf(stdout, nullptr);
  
    openlog(pgm_name, LOG_PID | LOG_NDELAY | LOG_NOWAIT, LOG_DAEMON);
  
    init_system(true);
    initialize_process_manager();
    diagsConfig = new DiagsConfig(pgm_name, logfile, error_tags, action_tags);
    diags       = diagsConfig->diags;
  }
  
  /*-------------------------------------------------------------------------
    init_log_standalone_basic
  
    This routine is similar to init_log_standalone, but it is intended for
    simple standalone applications that do not read the records.config file
    and that do not need a process manager, thus it:
  
    1) does not call initialize_process_manager
    2) initializes the diags with use_records = false
    3) does not call Machine::init()
    4) assumes multiple copies of the application can run, so does not
       do lock checking
    -------------------------------------------------------------------------*/
  
  void
  init_log_standalone_basic(const char *pgm_name)
  {
    char logfile[LOG_FILENAME_SIZE];
  
    snprintf(logfile, sizeof(logfile), "%s.log", pgm_name);
    openlog(pgm_name, LOG_PID | LOG_NDELAY | LOG_NOWAIT, LOG_DAEMON);
  
    init_system(false);
    const bool use_records = false;
    diagsConfig            = new DiagsConfig(pgm_name, logfile, error_tags, action_tags, use_records);
    diags                  = diagsConfig->diags;
    // set stdin/stdout to be unbuffered
    //
    setbuf(stdin, nullptr);
    setbuf(stdout, nullptr);
  }
  