]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Do not abuse argv[0] to supply roles and IDs to SMP kids (#176)
authorEduard Bagdasaryan <eduard.bagdasaryan@measurement-factory.com>
Mon, 30 Apr 2018 16:57:53 +0000 (16:57 +0000)
committerAmos Jeffries <yadij@users.noreply.github.com>
Wed, 9 May 2018 10:23:29 +0000 (22:23 +1200)
Use a newly added "--kid role-ID" command line option instead. Just like
argv[0], the new option is not meant for direct/human use.

This change allows exec(3)-wrapping tools like Valgrind to work with SMP
Squid: When launching kid processes, Valgrind does not pass Squid-formed
argv[0] to kid processes, breaking old kid role and ID detection code.

This change does not alter argv[0] of Squid processes. There is nothing
wrong with Squid-formed argv[0] values for Squid kids.

Also added a CommandLine class to support command line parsing without
code duplication. Squid needs to handle the new --kid option way before
the old mainParseOptions() handles the other options. The new class also
encapsulates argv manipulations, reducing main.cc pollution.

src/CommandLine.cc [new file with mode: 0644]
src/CommandLine.h [new file with mode: 0644]
src/Makefile.am
src/cache_cf.cc
src/ipc/Kid.cc
src/ipc/Kid.h
src/ipc/Kids.cc
src/ipc/Kids.h
src/main.cc
src/squid.8.in

diff --git a/src/CommandLine.cc b/src/CommandLine.cc
new file mode 100644 (file)
index 0000000..69bd2a7
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#include "squid.h"
+
+#include "CommandLine.h"
+#include "sbuf/SBuf.h"
+
+static void
+ResetGetopt(const bool allowStderrWarnings)
+{
+    opterr = allowStderrWarnings;
+    // Resetting optind to zero instead of conventional '1' has an
+    // advantage, since it also resets getopt(3) global state.
+    // getopt(3) always skips argv[0], even if optind is zero
+    optind = 0;
+}
+
+CommandLine::CommandLine(int argC, char *argV[], const char *shortRules, const RawLongOption *longRules):
+    argv_(),
+    shortOptions_(shortRules ? xstrdup(shortRules) : ""),
+    longOptions_()
+{
+    assert(argC > 0); // C++ main() requirement that makes our arg0() safe
+    assert(shortRules);
+
+    /* copy argV items */
+    argv_.reserve(argC+1);
+    for (int i = 0; i < argC; ++i)
+        argv_.push_back(xstrdup(argV[i]));
+    argv_.push_back(nullptr); // POSIX argv "must be terminated by a null pointer"
+
+    /* copy grammar rules for the long options */
+    if (longRules) {
+        for (auto longOption = longRules; longOption->name; ++longOption)
+            longOptions_.emplace_back(*longOption);
+        longOptions_.emplace_back();
+    }
+}
+
+CommandLine::CommandLine(const CommandLine &them):
+    CommandLine(them.argc(), them.argv(), them.shortOptions_, them.longOptions())
+{
+}
+
+CommandLine &
+CommandLine::operator =(const CommandLine &them)
+{
+    // cannot just swap(*this, them): std::swap(T,T) may call this assignment op
+    CommandLine tmp(them);
+    std::swap(argv_, tmp.argv_);
+    std::swap(shortOptions_, tmp.shortOptions_);
+    std::swap(longOptions_, tmp.longOptions_);
+    return *this;
+}
+
+CommandLine::~CommandLine()
+{
+    for (auto arg: argv_)
+        xfree(arg);
+
+    xfree(shortOptions_);
+}
+
+bool
+CommandLine::hasOption(const int optIdToFind, const char **optValue) const
+{
+    ResetGetopt(false); // avoid duped warnings; forEachOption() will complain
+    int optId = 0;
+    while (nextOption(optId)) {
+        if (optId == optIdToFind) {
+            if (optValue) {
+                // do not need to copy the optarg string because it is a pointer into the original
+                // argv array (https://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html)
+                *optValue = optarg;
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+void
+CommandLine::forEachOption(Visitor visitor) const
+{
+    ResetGetopt(true);
+    int optId = 0;
+    while (nextOption(optId))
+        visitor(optId, optarg);
+}
+
+/// extracts the next option (if any)
+/// \returns whether the option was extracted
+/// throws on unknown option or missing required argument
+bool
+CommandLine::nextOption(int &optId) const
+{
+    optId = getopt_long(argc(), argv(), shortOptions_, longOptions(), nullptr);
+    if ((optId == ':' && shortOptions_[0] == ':') || optId == '?') {
+        assert(optind > 0 && static_cast<unsigned int>(optind) < argv_.size());
+        SBuf errMsg;
+        errMsg.Printf("'%s': %s", argv_[optind - 1],  optId == '?' ?
+                      "unrecognized option or missing required argument" : "missing required argument");
+        throw TexcHere(errMsg);
+    }
+    return optId != -1;
+}
+
+void
+CommandLine::resetArg0(const char *programName)
+{
+    assert(programName);
+    xfree(argv_[0]);
+    argv_[0] = xstrdup(programName);
+}
+
+void
+CommandLine::pushFrontOption(const char *name, const char *value)
+{
+    assert(name);
+    argv_.insert(argv_.begin() + 1, xstrdup(name));
+    if (value)
+        argv_.insert(argv_.begin() + 2, xstrdup(value));
+}
+
+LongOption::LongOption() :
+    option({nullptr, 0, nullptr, 0})
+{
+}
+
+LongOption::LongOption(const RawLongOption &opt) :
+    option({nullptr, 0, nullptr, 0})
+{
+    copy(opt);
+}
+
+LongOption::LongOption(const LongOption &opt):
+    LongOption(static_cast<const RawLongOption &>(opt))
+{
+}
+
+LongOption::~LongOption()
+{
+    xfree(name);
+}
+
+LongOption &
+LongOption::operator =(const LongOption &opt)
+{
+    if (this != &opt)
+        copy(static_cast<const RawLongOption &>(opt));
+    return *this;
+}
+
+void
+LongOption::copy(const RawLongOption &opt)
+{
+    xfree(name);
+    name = opt.name ? xstrdup(opt.name) : nullptr;
+    has_arg = opt.has_arg;
+    flag = opt.flag;
+    val = opt.val;
+}
+
diff --git a/src/CommandLine.h b/src/CommandLine.h
new file mode 100644 (file)
index 0000000..0371dc7
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#ifndef SQUID_COMMANDLINE_H
+#define SQUID_COMMANDLINE_H
+
+#if HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#include <vector>
+
+typedef struct option RawLongOption;
+
+/// A struct option C++ wrapper, helps with option::name copying/freeing.
+class LongOption : public RawLongOption
+{
+public:
+    LongOption();
+    explicit LongOption(const RawLongOption &);
+    LongOption(const LongOption&);
+    LongOption &operator =(const LongOption &);
+    ~LongOption();
+
+private:
+    void copy(const RawLongOption &);
+};
+
+/// Manages arguments passed to a program (i.e., main(argc, argv) parameters).
+class CommandLine
+{
+public:
+    /// expects main() input plus getopt_long(3) grammar rules for parsing argv
+    CommandLine(int argc, char *argv[], const char *shortRules, const RawLongOption *longRules);
+    CommandLine(const CommandLine &them);
+    ~CommandLine();
+
+    CommandLine &operator =(const CommandLine &);
+
+    /// \returns whether the option with optId identifier is present
+    /// When returning true, sets non-nil optValue to the found option's value.
+    /// For letter options (-x) and their --long synonyms, the letter is the ID.
+    /// For long-only --options, the ID is the configured options::val value.
+    bool hasOption(const int optId, const char **optValue = nullptr) const;
+
+    /// A callback function for forEachOption(); receives parsed options.
+    /// Must not call pushFrontOption(), hasOption() or forEachOption() -- getopt(3) uses globals!
+    typedef void Visitor(const int optId, const char *optValue);
+
+    /// calls Visitor for each of the configured command line option
+    void forEachOption(Visitor) const;
+
+    /// \returns argv[0], which is usually a program "name"
+    const char *arg0() const { return argv_[0]; }
+
+    /// \returns main()'s argc, which is traditionally missing the last/nil item
+    int argc() const { return static_cast<int>(argv_.size()) - 1; }
+
+    /// \returns main()'s argv[] which is traditionally const-wrong
+    char **argv() const { return const_cast<char**>(argv_.data()); }
+
+    /// replaces argv[0] with the new value
+    void resetArg0(const char *programName);
+
+    /// inserts a (possibly duplicated) option at the beginning of options (just after argv[0])
+    void pushFrontOption(const char *name, const char *value = nullptr);
+
+private:
+    const RawLongOption *longOptions() const { return longOptions_.size() ? longOptions_.data() : nullptr; }
+    bool nextOption(int &optId) const;
+
+    /// raw main() parameters, including argv[0] and a nil argv[argc]
+    std::vector<char *> argv_;
+
+    /* getopt_long() grammar rules */
+    const char *shortOptions_; ///< single-dash, single-letter (-x) option rules
+    std::vector<LongOption> longOptions_; ///< long --option rules
+};
+
+#endif /* SQUID_COMMANDLINE_H */
+
index 30aa0ef702183873c1ef9abee82e63e11cf79def..acc0520089107d47ff057a6de0f91c9fd4c1f18d 100644 (file)
@@ -250,6 +250,8 @@ squid_SOURCES = \
        clientStreamForward.h \
        CollapsedForwarding.cc \
        CollapsedForwarding.h \
+       CommandLine.cc \
+       CommandLine.h \
        CompletionDispatcher.cc \
        CompletionDispatcher.h \
        CommRead.h \
index ad91538f244de05b8b915d678abb0db4f243f59d..66f23c016e20fb9656e351dc83f4a0ae9422f780 100644 (file)
@@ -338,7 +338,7 @@ static void
 ProcessMacros(char*& line, int& len)
 {
     SubstituteMacro(line, len, "${service_name}", service_name.c_str());
-    SubstituteMacro(line, len, "${process_name}", TheKidName);
+    SubstituteMacro(line, len, "${process_name}", TheKidName.c_str());
     SubstituteMacro(line, len, "${process_number}", xitoa(KidIdentifier));
 }
 
index 0a416743255ade9c4a1bc6f6242b8a2138a0edf9..439199836c3b762364bd74dc956e5a68332eaf5c 100644 (file)
 
 int TheProcessKind = pkOther;
 
-Kid::Kid():
-    badFailures(0),
-    pid(-1),
-    startTime(0),
-    isRunning(false),
-    status(0)
+Kid::Kid()
 {
 }
 
-Kid::Kid(const String& kid_name):
-    theName(kid_name),
-    badFailures(0),
-    pid(-1),
-    startTime(0),
-    isRunning(false),
-    status(0)
+Kid::Kid(const char *aRole, const int anId):
+    processRole(aRole),
+    processId(anId)
 {
 }
 
@@ -77,20 +68,20 @@ Kid::reportStopped() const
     if (calledExit()) {
         syslog(LOG_NOTICE,
                "Squid Parent: %s process %d exited with status %d",
-               theName.termedBuf(), pid, exitStatus());
+               gist().c_str(), pid, exitStatus());
     } else if (signaled()) {
         syslog(LOG_NOTICE,
                "Squid Parent: %s process %d exited due to signal %d with status %d",
-               theName.termedBuf(), pid, termSignal(), exitStatus());
+               gist().c_str(), pid, termSignal(), exitStatus());
     } else {
         syslog(LOG_NOTICE, "Squid Parent: %s process %d exited",
-               theName.termedBuf(), pid);
+               gist().c_str(), pid);
     }
 
     if (hopeless() && Config.hopelessKidRevivalDelay) {
         syslog(LOG_NOTICE, "Squid Parent: %s process %d will not be restarted for %ld "
                "seconds due to repeated, frequent failures",
-               theName.termedBuf(),
+               gist().c_str(),
                pid,
                static_cast<long int>(Config.hopelessKidRevivalDelay));
     }
@@ -170,9 +161,19 @@ bool Kid::signaled(int sgnl) const
 }
 
 /// returns kid name
-const String& Kid::name() const
+SBuf Kid::processName() const
 {
-    return theName;
+    SBuf name("(");
+    name.append(gist());
+    name.append(")");
+    return name;
+}
+
+SBuf Kid::gist() const
+{
+    SBuf name(processRole);
+    name.appendf("-%d", processId);
+    return name;
 }
 
 time_t
index 0eff062cda459f9c8b60420cd52470c6f8f200bb..d174c84a64974a9f4788c2be978c3bbfb0a49398 100644 (file)
@@ -27,7 +27,7 @@ public:
 public:
     Kid();
 
-    Kid(const String& kid_name);
+    Kid(const char *role, const int id);
 
     /// called when this kid got started, records PID
     void start(pid_t cpid);
@@ -74,22 +74,26 @@ public:
     /// whether the process was terminated by a given signal
     bool signaled(int sgnl) const;
 
-    /// returns kid name
-    const String& name() const;
+    /// \returns kid's role and ID formatted for use as a process name
+    SBuf processName() const;
+
+    /// \returns kid's role and ID summary; usable as a --kid parameter value
+    SBuf gist() const;
 
 private:
     void reportStopped() const;
 
     // Information preserved across restarts
-    String theName; ///< process name
-    int badFailures; ///< number of "repeated frequent" failures
+    SBuf processRole;
+    int processId = 0;
+    int badFailures = 0; ///< number of "repeated frequent" failures
 
     // Information specific to a running or stopped kid
-    pid_t  pid; ///< current (for a running kid) or last (for stopped kid) PID
-    time_t startTime; ///< last start time
+    pid_t  pid = -1; ///< current (for a running kid) or last (for stopped kid) PID
+    time_t startTime = 0; ///< last start time
     time_t stopTime = 0; ///< last termination time
-    bool   isRunning; ///< whether the kid is assumed to be alive
-    PidStatus status; ///< exit status of a stopped kid
+    bool isRunning = false; ///< whether the kid is assumed to be alive
+    PidStatus status = 0; ///< exit status of a stopped kid
 };
 
 // TODO: processes may not be kids; is there a better place to put this?
index 6911414a197ba3cb80c39e39776deb87c7f63609..249df11d6a9283c7e5693447b66227022b0447f5 100644 (file)
@@ -16,7 +16,7 @@
 #include "tools.h"
 
 Kids TheKids;
-KidName TheKidName;
+SBuf TheKidName;
 
 Kids::Kids()
 {
@@ -29,25 +29,16 @@ void Kids::init()
 
     storage.reserve(NumberOfKids());
 
-    char kid_name[32];
-
-    // add Kid records for all workers
-    for (int i = 0; i < Config.workers; ++i) {
-        snprintf(kid_name, sizeof(kid_name), "(squid-%d)", (int)(storage.size()+1));
-        storage.push_back(Kid(kid_name));
-    }
+    for (int i = 0; i < Config.workers; ++i)
+        storage.emplace_back("squid", storage.size() + 1);
 
     // add Kid records for all disk processes
-    for (int i = 0; i < Config.cacheSwap.n_strands; ++i) {
-        snprintf(kid_name, sizeof(kid_name), "(squid-disk-%d)", (int)(storage.size()+1));
-        storage.push_back(Kid(kid_name));
-    }
+    for (int i = 0; i < Config.cacheSwap.n_strands; ++i)
+        storage.emplace_back("squid-disk", storage.size() + 1);
 
     // if coordination is needed, add a Kid record for Coordinator
-    if (storage.size() > 1) {
-        snprintf(kid_name, sizeof(kid_name), "(squid-coord-%d)", (int)(storage.size()+1));
-        storage.push_back(Kid(kid_name));
-    }
+    if (storage.size() > 1)
+        storage.emplace_back("squid-coord", storage.size() + 1);
 
     Must(storage.size() == static_cast<size_t>(NumberOfKids()));
 }
index 89f49ff0b7a90d2f378ec5203711e46bce41b9a7..03b12b1fe9623c96971e9dadfad0f8f09b8abdfc 100644 (file)
@@ -64,8 +64,7 @@ private:
 
 extern Kids TheKids; ///< All kids being maintained
 
-typedef char KidName[64]; ///< Squid process name (e.g., "squid-coord")
-extern KidName TheKidName; ///< current Squid process name
+extern SBuf TheKidName; ///< current Squid process name (e.g., "squid-coord")
 
 #endif /* SQUID_IPC_KIDS_H */
 
index bb7ca6d00e5e07a9917b32817f83111f256ebb6d..0e17be587cb689ee17e3c589b98602ebe4ea1595 100644 (file)
@@ -24,6 +24,7 @@
 #include "client_db.h"
 #include "client_side.h"
 #include "comm.h"
+#include "CommandLine.h"
 #include "ConfigParser.h"
 #include "CpuAffinity.h"
 #include "DiskIO/DiskIOModule.h"
@@ -56,6 +57,7 @@
 #include "mime.h"
 #include "neighbors.h"
 #include "parser/Tokenizer.h"
+#include "Parsing.h"
 #include "pconn.h"
 #include "peer_sourcehash.h"
 #include "peer_userhash.h"
@@ -168,11 +170,11 @@ static void mainReconfigureStart(void);
 static void mainReconfigureFinish(void*);
 static void mainInitialize(void);
 static void usage(void);
-static void mainParseOptions(int argc, char *argv[]);
+static void mainHandleCommandLineOption(const int optId, const char *optValue);
 static void sendSignal(void);
 static void serverConnectionsOpen(void);
 static void serverConnectionsClose(void);
-static void watch_child(char **);
+static void watch_child(const CommandLine &);
 static void setEffectiveUser(void);
 static void SquidShutdown(void);
 static void mainSetCwd(void);
@@ -393,6 +395,9 @@ usage(void)
             "       -N        Master process runs in foreground and is a worker. No kids.\n"
             "       --foreground\n"
             "                 Master process runs in foreground and creates worker kids.\n"
+            "       --kid role-ID\n"
+            "                 Play a given SMP kid process role, with a given ID. Do not use\n"
+            "                 this option. It is meant for the master process use only.\n"
 #if USE_WIN32_SERVICE
             "       -O options\n"
             "                 Set Windows Service Command line options in Registry.\n"
@@ -402,311 +407,320 @@ usage(void)
             "       -X        Force full debugging.\n"
             "       -Y        Only return UDP_HIT or UDP_MISS_NOFETCH during fast reload.\n",
             APP_SHORTNAME, CACHE_HTTP_PORT, DefaultConfigFile, CACHE_ICP_PORT);
-    exit(1);
+    exit(EXIT_FAILURE);
 }
 
-/**
- * Parse the parameters received via command line interface.
- *
- * \param argc   Number of options received on command line
- * \param argv   List of parameters received on command line
- */
-static void
-mainParseOptions(int argc, char *argv[])
-{
-    int optIndex = 0;
+/// CommandLine option IDs for --long options that lack a short (-x) equivalent
+enum {
+    // The absolute values do not matter except that the following values should
+    // not be used: Values below 2 are for special getopt_long(3) use cases, and
+    // values in the [33,126] range are reserved for short options (-x).
+    optForeground = 2,
+    optKid
+};
 
-    // short options
-    const char *shortOpStr =
+// short options
+// TODO: consider prefixing with ':' for better logging
+// (distinguish missing required argument cases)
+static const char *shortOpStr =
 #if USE_WIN32_SERVICE
-        "O:Vir"
+    "O:Vir"
 #endif
-        "CDFNRSYXa:d:f:hk:m::n:sl:u:vz?";
-
-    // long options
-    static struct option squidOptions[] = {
-        {"foreground", no_argument, 0,  1 },
-        {"help",       no_argument, 0, 'h'},
-        {"version",    no_argument, 0, 'v'},
-        {0, 0, 0, 0}
-    };
-
-    int c;
-    while ((c = getopt_long(argc, argv, shortOpStr, squidOptions, &optIndex)) != -1) {
-
-        switch (c) {
+    "CDFNRSYXa:d:f:hk:m::n:sl:u:vz?";
 
-        case 'C':
-            /** \par C
-             * Unset/disabel global option for catchign signals. opt_catch_signals */
-            opt_catch_signals = 0;
-            break;
+// long options
+static struct option squidOptions[] = {
+    {"foreground", no_argument, 0,  optForeground},
+    {"kid",        required_argument, 0, optKid},
+    {"help",       no_argument, 0, 'h'},
+    {"version",    no_argument, 0, 'v'},
+    {0, 0, 0, 0}
+};
 
-        case 'D':
-            /** \par D
-             * OBSOLETE: WAS: override to prevent optional startup DNS tests. */
-            debugs(1,DBG_CRITICAL, "WARNING: -D command-line option is obsolete.");
-            break;
+// handle a command line parameter
+static void
+mainHandleCommandLineOption(const int optId, const char *optValue)
+{
+    switch (optId) {
+
+    case 'C':
+        /** \par C
+         * Unset/disabel global option for catchign signals. opt_catch_signals */
+        opt_catch_signals = 0;
+        break;
+
+    case 'D':
+        /** \par D
+         * OBSOLETE: WAS: override to prevent optional startup DNS tests. */
+        debugs(1,DBG_CRITICAL, "WARNING: -D command-line option is obsolete.");
+        break;
+
+    case 'F':
+        /** \par F
+         * Set global option for foreground rebuild. opt_foreground_rebuild */
+        opt_foreground_rebuild = 1;
+        break;
+
+    case 'N':
+        /** \par N
+         * Set global option for 'no_daemon' mode. opt_no_daemon */
+        opt_no_daemon = 1;
+        break;
 
-        case 'F':
-            /** \par F
-             * Set global option for foreground rebuild. opt_foreground_rebuild */
-            opt_foreground_rebuild = 1;
-            break;
+#if USE_WIN32_SERVICE
 
-        case 'N':
-            /** \par N
-             * Set global option for 'no_daemon' mode. opt_no_daemon */
-            opt_no_daemon = 1;
-            break;
+    case 'O':
+        /** \par O
+         * Set global option. opt_command_lin and WIN32_Command_Line */
+        opt_command_line = 1;
+        WIN32_Command_Line = xstrdup(optValue);
+        break;
+#endif
+
+    case 'R':
+        /** \par R
+         * Unset/disable global option opt_reuseaddr */
+        opt_reuseaddr = 0;
+        break;
+
+    case 'S':
+        /** \par S
+         * Set global option opt_store_doublecheck */
+        opt_store_doublecheck = 1;
+        break;
+
+    case 'X':
+        /** \par X
+         * Force full debugging */
+        Debug::parseOptions("rotate=0 ALL,9");
+        Debug::override_X = 1;
+        sigusr2_handle(SIGUSR2);
+        break;
+
+    case 'Y':
+        /** \par Y
+         * Set global option opt_reload_hit_only */
+        opt_reload_hit_only = 1;
+        break;
 
 #if USE_WIN32_SERVICE
 
-        case 'O':
-            /** \par O
-             * Set global option. opt_command_lin and WIN32_Command_Line */
-            opt_command_line = 1;
-            WIN32_Command_Line = xstrdup(optarg);
-            break;
-#endif
-
-        case 'R':
-            /** \par R
-             * Unset/disable global option opt_reuseaddr */
-            opt_reuseaddr = 0;
-            break;
-
-        case 'S':
-            /** \par S
-             * Set global option opt_store_doublecheck */
-            opt_store_doublecheck = 1;
-            break;
-
-        case 'X':
-            /** \par X
-             * Force full debugging */
-            Debug::parseOptions("rotate=0 ALL,9");
-            Debug::override_X = 1;
-            sigusr2_handle(SIGUSR2);
-            break;
-
-        case 'Y':
-            /** \par Y
-             * Set global option opt_reload_hit_only */
-            opt_reload_hit_only = 1;
-            break;
+    case 'i':
+        /** \par i
+         * Set global option opt_install_service (to TRUE) */
+        opt_install_service = TRUE;
+        break;
+#endif
 
-#if USE_WIN32_SERVICE
+    case 'a':
+    {
+        /** \par a
+         * Add optional HTTP port as given following the option */
+        char *port = xstrdup(optValue);
+        // use a copy to avoid optValue modification
+        add_http_port(port);
+        xfree(port);
+        break;
+    }
 
-        case 'i':
-            /** \par i
-             * Set global option opt_install_service (to TRUE) */
-            opt_install_service = TRUE;
-            break;
-#endif
-
-        case 'a':
-            /** \par a
-             * Add optional HTTP port as given following the option */
-            add_http_port(optarg);
-            break;
-
-        case 'd':
-            /** \par d
-             * Set global option Debug::log_stderr to the number given following the option */
-            Debug::log_stderr = atoi(optarg);
-            break;
-
-        case 'f':
-            /** \par f
-             * Load the file given instead of the default squid.conf. */
-            xfree(ConfigFile);
-            ConfigFile = xstrdup(optarg);
-            break;
-
-        case 'k':
-            /** \par k
-             * Run the administrative action given following the option */
-
-            /** \li When it is missing or an unknown option display the usage help. */
-            if (!optarg || strlen(optarg) < 1)
-                usage();
-
-            else if (!strncmp(optarg, "reconfigure", strlen(optarg)))
-                /** \li On reconfigure send SIGHUP. */
-                opt_send_signal = SIGHUP;
-            else if (!strncmp(optarg, "rotate", strlen(optarg)))
-                /** \li On rotate send SIGQUIT or SIGUSR1. */
+    case 'd':
+        /** \par d
+         * Set global option Debug::log_stderr to the number given following the option */
+        Debug::log_stderr = xatoi(optValue);
+        break;
+
+    case 'f':
+        /** \par f
+         * Load the file given instead of the default squid.conf. */
+        xfree(ConfigFile);
+        ConfigFile = xstrdup(optValue);
+        break;
+
+    case 'k':
+        /** \par k
+         * Run the administrative action given following the option */
+
+        /** \li When it is missing or an unknown option display the usage help. */
+        if (!optValue || strlen(optValue) < 1)
+            usage();
+
+        else if (!strncmp(optValue, "reconfigure", strlen(optValue)))
+            /** \li On reconfigure send SIGHUP. */
+            opt_send_signal = SIGHUP;
+        else if (!strncmp(optValue, "rotate", strlen(optValue)))
+            /** \li On rotate send SIGQUIT or SIGUSR1. */
 #if defined(_SQUID_LINUX_THREADS_)
-                opt_send_signal = SIGQUIT;
+            opt_send_signal = SIGQUIT;
 #else
-                opt_send_signal = SIGUSR1;
+            opt_send_signal = SIGUSR1;
 #endif
 
-            else if (!strncmp(optarg, "debug", strlen(optarg)))
-                /** \li On debug send SIGTRAP or SIGUSR2. */
+        else if (!strncmp(optValue, "debug", strlen(optValue)))
+            /** \li On debug send SIGTRAP or SIGUSR2. */
 #if defined(_SQUID_LINUX_THREADS_)
-                opt_send_signal = SIGTRAP;
+            opt_send_signal = SIGTRAP;
 #else
-                opt_send_signal = SIGUSR2;
+            opt_send_signal = SIGUSR2;
 #endif
 
-            else if (!strncmp(optarg, "shutdown", strlen(optarg)))
-                /** \li On shutdown send SIGTERM. */
-                opt_send_signal = SIGTERM;
-            else if (!strncmp(optarg, "interrupt", strlen(optarg)))
-                /** \li On interrupt send SIGINT. */
-                opt_send_signal = SIGINT;
-            else if (!strncmp(optarg, "kill", strlen(optarg)))
-                /** \li On kill send SIGKILL. */
-                opt_send_signal = SIGKILL;
+        else if (!strncmp(optValue, "shutdown", strlen(optValue)))
+            /** \li On shutdown send SIGTERM. */
+            opt_send_signal = SIGTERM;
+        else if (!strncmp(optValue, "interrupt", strlen(optValue)))
+            /** \li On interrupt send SIGINT. */
+            opt_send_signal = SIGINT;
+        else if (!strncmp(optValue, "kill", strlen(optValue)))
+            /** \li On kill send SIGKILL. */
+            opt_send_signal = SIGKILL;
 
 #ifdef SIGTTIN
 
-            else if (!strncmp(optarg, "restart", strlen(optarg)))
-                /** \li On restart send SIGTTIN. (exit and restart by parent) */
-                opt_send_signal = SIGTTIN;
+        else if (!strncmp(optValue, "restart", strlen(optValue)))
+            /** \li On restart send SIGTTIN. (exit and restart by parent) */
+            opt_send_signal = SIGTTIN;
 
 #endif
 
-            else if (!strncmp(optarg, "check", strlen(optarg)))
-                /** \li On check send 0 / SIGNULL. */
-                opt_send_signal = 0;    /* SIGNULL */
-            else if (!strncmp(optarg, "parse", strlen(optarg)))
-                /** \li On parse set global flag to re-parse the config file only. */
-                opt_parse_cfg_only = 1;
-            else
-                usage();
+        else if (!strncmp(optValue, "check", strlen(optValue)))
+            /** \li On check send 0 / SIGNULL. */
+            opt_send_signal = 0;    /* SIGNULL */
+        else if (!strncmp(optValue, "parse", strlen(optValue)))
+            /** \li On parse set global flag to re-parse the config file only. */
+            opt_parse_cfg_only = 1;
+        else
+            usage();
 
-            break;
+        break;
 
-        case 'm':
-            /** \par m
-             * Set global malloc_debug_level to the value given following the option.
-             * if none is given it toggles the xmalloc_trace option on/off */
-            if (optarg) {
+    case 'm':
+        /** \par m
+         * Set global malloc_debug_level to the value given following the option.
+         * if none is given it toggles the xmalloc_trace option on/off */
+        if (optValue) {
 #if MALLOC_DBG
-                malloc_debug_level = atoi(optarg);
+            malloc_debug_level = xatoi(optValue);
 #else
-                fatal("Need to add -DMALLOC_DBG when compiling to use -mX option");
+            fatal("Need to add -DMALLOC_DBG when compiling to use -mX option");
 #endif
 
-            }
-            break;
-
-        case 'n':
-            /** \par n
-             * Set global option opt_signal_service (to true).
-             * Stores the additional parameter given in global service_name */
-            if (optarg && *optarg != '\0') {
-                const SBuf t(optarg);
-                ::Parser::Tokenizer tok(t);
-                const CharacterSet chr = CharacterSet::ALPHA+CharacterSet::DIGIT;
-                if (!tok.prefix(service_name, chr))
-                    fatalf("Expected alphanumeric service name for the -n option but got: %s", optarg);
-                if (!tok.atEnd())
-                    fatalf("Garbage after alphanumeric service name in the -n option value: %s", optarg);
-                if (service_name.length() > 32)
-                    fatalf("Service name (-n option) must be limited to 32 characters but got %u", service_name.length());
-                opt_signal_service = true;
-            } else {
-                fatal("A service name is required for the -n option");
-            }
-            break;
+        }
+        break;
+
+    case 'n':
+        /** \par n
+         * Set global option opt_signal_service (to true).
+         * Stores the additional parameter given in global service_name */
+        if (optValue && *optValue != '\0') {
+            const SBuf t(optValue);
+            ::Parser::Tokenizer tok(t);
+            const CharacterSet chr = CharacterSet::ALPHA+CharacterSet::DIGIT;
+            if (!tok.prefix(service_name, chr))
+                fatalf("Expected alphanumeric service name for the -n option but got: %s", optValue);
+            if (!tok.atEnd())
+                fatalf("Garbage after alphanumeric service name in the -n option value: %s", optValue);
+            if (service_name.length() > 32)
+                fatalf("Service name (-n option) must be limited to 32 characters but got %u", service_name.length());
+            opt_signal_service = true;
+        } else {
+            fatal("A service name is required for the -n option");
+        }
+        break;
 
 #if USE_WIN32_SERVICE
 
-        case 'r':
-            /** \par r
-             * Set global option opt_remove_service (to TRUE) */
-            opt_remove_service = TRUE;
+    case 'r':
+        /** \par r
+         * Set global option opt_remove_service (to TRUE) */
+        opt_remove_service = TRUE;
 
-            break;
+        break;
 
 #endif
 
-        case 'l':
-            /** \par l
-             * Stores the syslog facility name in global opt_syslog_facility
-             * then performs actions for -s option. */
-            xfree(opt_syslog_facility); // ignore any previous options sent
-            opt_syslog_facility = xstrdup(optarg);
+    case 'l':
+        /** \par l
+         * Stores the syslog facility name in global opt_syslog_facility
+         * then performs actions for -s option. */
+        xfree(opt_syslog_facility); // ignore any previous options sent
+        opt_syslog_facility = xstrdup(optValue);
 
-        case 's':
-            /** \par s
-             * Initialize the syslog for output */
+    case 's':
+        /** \par s
+         * Initialize the syslog for output */
 #if HAVE_SYSLOG
 
-            _db_set_syslog(opt_syslog_facility);
+        _db_set_syslog(opt_syslog_facility);
 
-            break;
+        break;
 
 #else
 
-            fatal("Logging to syslog not available on this platform");
+        fatal("Logging to syslog not available on this platform");
 
-            /* NOTREACHED */
+        /* NOTREACHED */
 #endif
 
-        case 'u':
-            /** \par u
-             * Store the ICP port number given in global option icpPortNumOverride
-             * ensuring its a positive number. */
-            icpPortNumOverride = atoi(optarg);
+    case 'u':
+        /** \par u
+         * Store the ICP port number given in global option icpPortNumOverride
+         * ensuring its a positive number. */
+        icpPortNumOverride = atoi(optValue);
 
-            if (icpPortNumOverride < 0)
-                icpPortNumOverride = 0;
+        if (icpPortNumOverride < 0)
+            icpPortNumOverride = 0;
 
-            break;
+        break;
 
-        case 'v':
-            /** \par v
-             * Display squid version and build information. Then exit. */
-            printf("Squid Cache: Version %s\n" ,version_string);
-            printf("Service Name: " SQUIDSBUFPH "\n", SQUIDSBUFPRINT(service_name));
-            if (strlen(SQUID_BUILD_INFO))
-                printf("%s\n",SQUID_BUILD_INFO);
+    case 'v':
+        /** \par v
+         * Display squid version and build information. Then exit. */
+        printf("Squid Cache: Version %s\n",version_string);
+        printf("Service Name: " SQUIDSBUFPH "\n", SQUIDSBUFPRINT(service_name));
+        if (strlen(SQUID_BUILD_INFO))
+            printf("%s\n",SQUID_BUILD_INFO);
 #if USE_OPENSSL
-            printf("\nThis binary uses %s. ", SSLeay_version(SSLEAY_VERSION));
-            printf("For legal restrictions on distribution see https://www.openssl.org/source/license.html\n\n");
+        printf("\nThis binary uses %s. ", SSLeay_version(SSLEAY_VERSION));
+        printf("For legal restrictions on distribution see https://www.openssl.org/source/license.html\n\n");
 #endif
-            printf( "configure options: %s\n", SQUID_CONFIGURE_OPTIONS);
+        printf( "configure options: %s\n", SQUID_CONFIGURE_OPTIONS);
 
 #if USE_WIN32_SERVICE
 
-            printf("Compiled as Windows System Service.\n");
+        printf("Compiled as Windows System Service.\n");
 
 #endif
 
-            exit(0);
+        exit(EXIT_SUCCESS);
 
-        /* NOTREACHED */
+    /* NOTREACHED */
 
-        case 'z':
-            /** \par z
-             * Set global option Debug::log_stderr and opt_create_swap_dirs */
-            Debug::log_stderr = 1;
-            opt_create_swap_dirs = 1;
-            break;
+    case 'z':
+        /** \par z
+         * Set global option Debug::log_stderr and opt_create_swap_dirs */
+        Debug::log_stderr = 1;
+        opt_create_swap_dirs = 1;
+        break;
 
-        case 1:
-            /** \par --foreground
-             * Set global option opt_foreground */
-            opt_foreground = 1;
-            break;
+    case optForeground:
+        /** \par --foreground
+         * Set global option opt_foreground */
+        opt_foreground = 1;
+        break;
 
-        case 'h':
+    case optKid:
+        // already processed in ConfigureCurrentKid()
+        break;
 
-        case '?':
+    case 'h':
 
-        default:
-            /** \par h,?, or unknown
-             * \copydoc usage() */
-            usage();
+    case '?':
 
-            break;
-        }
+    default:
+        /** \par h,?, or unknown
+         * \copydoc usage() */
+        usage();
 
+        break;
     }
 }
 
@@ -1408,26 +1422,27 @@ SquidMainSafe(int argc, char **argv)
 
 /// computes name and ID for the current kid process
 static void
-ConfigureCurrentKid(const char *processName)
+ConfigureCurrentKid(const CommandLine &cmdLine)
 {
-    // kids are marked with parenthesis around their process names
-    if (processName && processName[0] == '(') {
-        if (const char *idStart = strrchr(processName, '-')) {
-            KidIdentifier = atoi(idStart + 1);
-            const size_t nameLen = idStart - (processName + 1);
-            assert(nameLen < sizeof(TheKidName));
-            xstrncpy(TheKidName, processName + 1, nameLen + 1);
-            if (!strcmp(TheKidName, "squid-coord"))
-                TheProcessKind = pkCoordinator;
-            else if (!strcmp(TheKidName, "squid"))
-                TheProcessKind = pkWorker;
-            else if (!strcmp(TheKidName, "squid-disk"))
-                TheProcessKind = pkDisker;
-            else
-                TheProcessKind = pkOther; // including coordinator
-        }
+    const char *kidParams = nullptr;
+    if (cmdLine.hasOption(optKid, &kidParams)) {
+        SBuf processName(kidParams);
+        SBuf kidId;
+        Parser::Tokenizer tok(processName);
+        tok.suffix(kidId, CharacterSet::DIGIT);
+        KidIdentifier = xatoi(kidId.c_str());
+        tok.skipSuffix(SBuf("-"));
+        TheKidName = tok.remaining();
+        if (TheKidName.cmp("squid-coord") == 0)
+            TheProcessKind = pkCoordinator;
+        else if (TheKidName.cmp("squid") == 0)
+            TheProcessKind = pkWorker;
+        else if (TheKidName.cmp("squid-disk") == 0)
+            TheProcessKind = pkDisker;
+        else
+            TheProcessKind = pkOther; // including coordinator
     } else {
-        xstrncpy(TheKidName, APP_SHORTNAME, sizeof(TheKidName));
+        TheKidName.assign(APP_SHORTNAME);
         KidIdentifier = 0;
     }
 }
@@ -1441,7 +1456,9 @@ static void StartUsingConfig()
 int
 SquidMain(int argc, char **argv)
 {
-    ConfigureCurrentKid(argv[0]);
+    const CommandLine cmdLine(argc, argv, shortOpStr, squidOptions);
+
+    ConfigureCurrentKid(cmdLine);
 
     Debug::parseOptions(NULL);
 
@@ -1488,7 +1505,7 @@ SquidMain(int argc, char **argv)
 
 #endif
 
-    mainParseOptions(argc, argv);
+    cmdLine.forEachOption(mainHandleCommandLineOption);
 
     if (opt_foreground && opt_no_daemon) {
         debugs(1, DBG_CRITICAL, "WARNING: --foreground command-line option has no effect with -N.");
@@ -1604,7 +1621,7 @@ SquidMain(int argc, char **argv)
 
     if (IamMasterProcess()) {
         if (InDaemonMode()) {
-            watch_child(argv);
+            watch_child(cmdLine);
             // NOTREACHED
         } else {
             Instance::WriteOurPid();
@@ -1877,10 +1894,9 @@ masterExit()
 #endif /* !_SQUID_WINDOWS_ */
 
 static void
-watch_child(char *argv[])
+watch_child(const CommandLine &masterCommand)
 {
 #if !_SQUID_WINDOWS_
-    char *prog;
     pid_t pid;
 #ifdef TIOCNOTTY
 
@@ -1984,23 +2000,28 @@ watch_child(char *argv[])
                 continue;
 
             if (!mainStartScriptCalled) {
-                mainStartScript(argv[0]);
+                mainStartScript(masterCommand.arg0());
                 mainStartScriptCalled = true;
             }
 
+            // These are only needed by the forked child below, but let's keep
+            // them out of that "no man's land" between fork() and execvp().
+            auto kidCommand = masterCommand;
+            kidCommand.resetArg0(kid.processName().c_str());
+            assert(!kidCommand.hasOption(optKid));
+            kidCommand.pushFrontOption("--kid", kid.gist().c_str());
+
             if ((pid = fork()) == 0) {
                 /* child */
                 openlog(APP_SHORTNAME, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_LOCAL4);
-                prog = argv[0];
-                argv[0] = const_cast<char*>(kid.name().termedBuf());
-                execvp(prog, argv);
+                (void)execvp(masterCommand.arg0(), kidCommand.argv());
                 int xerrno = errno;
                 syslog(LOG_ALERT, "execvp failed: %s", xstrerr(xerrno));
             }
 
             kid.start(pid);
             syslog(LOG_NOTICE, "Squid Parent: %s process %d started",
-                   kid.name().termedBuf(), pid);
+                   kid.processName().c_str(), pid);
         }
 
         /* parent */
index 5665833e2eabc2653c5ae157637d2e4dd0c9d64e..b760c95fe6075bc4e0e5176946f494c0beebf5bd 100644 (file)
@@ -124,6 +124,11 @@ Parent process does not exit until its children have finished. It has no effect
 which does not fork/exit at startup.
 .
 .if !'po4a'hide' .TP
+.if !'po4a'hide' .B "\--kid roleID"
+Play a given SMP kid process role, with a given ID. Do not use
+this option. It is meant for the master process use only.
+.
+.if !'po4a'hide' .TP
 .if !'po4a'hide' .B "\-O options"
 Set Windows Service Command line options in Registry.
 .