Drizzled Public API Documentation

signal_handler.cc
00001 /* 
00002    Copyright (C) 2011 Brian Aker
00003    Copyright (C) 2006 MySQL AB
00004 
00005    This program is free software; you can redistribute it and/or modify
00006    it under the terms of the GNU General Public License as published by
00007    the Free Software Foundation; version 2 of the License.
00008 
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012    GNU General Public License for more details.
00013 
00014    You should have received a copy of the GNU General Public License
00015    along with this program; if not, write to the Free Software
00016    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00017 
00018 #include <config.h>
00019 #include <drizzled/gettext.h>
00020 #include <drizzled/error.h>
00021 #include <drizzled/plugin/storage_engine.h>
00022 #include <drizzled/pthread_globals.h>
00023 #include <drizzled/internal/my_pthread.h>
00024 #include <drizzled/internal/my_sys.h>
00025 #include <drizzled/plugin/daemon.h>
00026 #include <drizzled/signal_handler.h>
00027 #include <drizzled/session.h>
00028 #include <drizzled/session/cache.h>
00029 #include <drizzled/debug.h>
00030 #include <drizzled/drizzled.h>
00031 #include <drizzled/open_tables_state.h>
00032 
00033 #include <boost/thread/thread.hpp>
00034 #include <boost/filesystem.hpp>
00035 
00036 #include <sys/stat.h>
00037 #include <fcntl.h>
00038 
00039 static bool kill_in_progress= false;
00040 void signal_hand(void);
00041 
00042 namespace drizzled {
00043 
00044 extern int cleanup_done;
00045 extern bool volatile abort_loop;
00046 extern bool volatile shutdown_in_progress;
00047 extern boost::filesystem::path pid_file;
00048 /* Prototypes -> all of these should be factored out into a propper shutdown */
00049 extern void close_connections(void);
00050 }
00051 
00052 using namespace drizzled;
00053 
00054 
00055 
00056 
00068 static void kill_server(int sig)
00069 {
00070   // if there is a signal during the kill in progress, ignore the other
00071   if (kill_in_progress)       // Safety
00072   {
00073     return;
00074   }
00075 
00076   kill_in_progress= true;
00077   abort_loop= true;         // This should be set
00078   if (sig != 0) // 0 is not a valid signal number
00079   {
00080     ignore_signal(sig);                    /* purify inspected */
00081   }
00082 
00083   if (sig == SIGTERM || sig == 0)
00084   {
00085     errmsg_printf(error::INFO, _(ER(ER_NORMAL_SHUTDOWN)),internal::my_progname);
00086   }
00087   else
00088   {
00089     errmsg_printf(error::ERROR, _(ER(ER_GOT_SIGNAL)),internal::my_progname,sig);
00090   }
00091 
00092   close_connections();
00093   clean_up(1);
00094 }
00095 
00099 static void create_pid_file()
00100 {
00101   int file;
00102   char buff[1024];
00103 
00104   if ((file = open(pid_file.file_string().c_str(), O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU|S_IRGRP|S_IROTH)) > 0)
00105   {
00106     int length;
00107 
00108     length= snprintf(buff, 1024, "%ld\n", (long) getpid()); 
00109 
00110     if ((write(file, buff, length)) == length)
00111     {
00112       if (close(file) != -1)
00113         return;
00114     }
00115     (void)close(file); /* We can ignore the error, since we are going to error anyway at this point */
00116   }
00117   memset(buff, 0, sizeof(buff));
00118   snprintf(buff, sizeof(buff)-1, "Can't start server: can't create PID file (%s)", pid_file.file_string().c_str());
00119   sql_perror(buff);
00120   exit(EXIT_FAILURE);
00121 }
00122 
00123 
00125 void signal_hand()
00126 {
00127   sigset_t set;
00128   int sig;
00129   internal::my_thread_init();       // Init new thread
00130   signal_thread_in_use= true;
00131 
00132   if ((drizzled::getDebug().test(drizzled::debug::ALLOW_SIGINT)))
00133   {
00134     (void) sigemptyset(&set);     // Setup up SIGINT for debug
00135     (void) sigaddset(&set,SIGINT);    // For debugging
00136     (void) pthread_sigmask(SIG_UNBLOCK, &set, NULL);
00137   }
00138   (void) sigemptyset(&set);     // Setup up SIGINT for debug
00139 #ifndef IGNORE_SIGHUP_SIGQUIT
00140   if (sigaddset(&set,SIGQUIT))
00141   {
00142     std::cerr << "failed setting sigaddset() with SIGQUIT\n";
00143   }
00144   if (sigaddset(&set,SIGHUP))
00145   {
00146     std::cerr << "failed setting sigaddset() with SIGHUP\n";
00147   }
00148 #endif
00149   if (sigaddset(&set,SIGTERM))
00150   {
00151     std::cerr << "failed setting sigaddset() with SIGTERM\n";
00152   }
00153   if (sigaddset(&set,SIGTSTP))
00154   {
00155     std::cerr << "failed setting sigaddset() with SIGTSTP\n";
00156   }
00157 
00158   /* Save pid to this process (or thread on Linux) */
00159   create_pid_file();
00160 
00161   /*
00162     signal to init that we are ready
00163     This works by waiting for init to free mutex,
00164     after which we signal it that we are ready.
00165     At this pointer there is no other threads running, so there
00166     should not be any other pthread_cond_signal() calls.
00167 
00168     We call lock/unlock to out wait any thread/session which is
00169     dieing. Since only comes from this code, this should be safe.
00170     (Asked MontyW over the phone about this.) -Brian
00171 
00172   */
00173   session::Cache::mutex().lock();
00174   session::Cache::mutex().unlock();
00175   COND_thread_count.notify_all();
00176 
00177   if (pthread_sigmask(SIG_BLOCK, &set, NULL))
00178   {
00179     std::cerr << "Failed to set pthread_sigmask() in signal handler\n";
00180   }
00181 
00182   for (;;)
00183   {
00184     if (shutdown_in_progress && not abort_loop)
00185     {
00186       sig= SIGTERM;
00187     }
00188     else
00189     {
00190       while (sigwait(&set, &sig) == EINTR) 
00191       {
00192       }
00193     }
00194 
00195     if (cleanup_done)
00196     {
00197       signal_thread_in_use= false;
00198       return;
00199     }
00200     switch (sig) 
00201     {
00202     case SIGTERM:
00203     case SIGQUIT:
00204     case SIGKILL:
00205     case SIGTSTP:
00206       /* switch to the old log message processing */
00207       if (!abort_loop)
00208       {
00209         abort_loop= 1;        // mark abort for threads
00210         kill_server(sig);   // MIT THREAD has a alarm thread
00211       }
00212       break;
00213     case SIGHUP:
00214       if (not abort_loop)
00215       {
00216         g_refresh_version++;
00217         drizzled::plugin::StorageEngine::flushLogs(NULL);
00218       }
00219       break;
00220     }
00221   }
00222 }
00223 
00224 class SignalHandler :
00225   public drizzled::plugin::Daemon
00226 {
00227   boost::thread thread;
00228 
00229 public:
00230   SignalHandler() :
00231     drizzled::plugin::Daemon("Signal Handler")
00232   {
00233     // @todo fix spurious wakeup issue
00234     boost::mutex::scoped_lock scopedLock(session::Cache::mutex());
00235     thread= boost::thread(signal_hand);
00236     signal_thread= thread.native_handle();
00237     COND_thread_count.wait(scopedLock);
00238   }
00239 
00244   ~SignalHandler()
00245   {
00246     /*
00247       Wait up to 100000 micro-seconds for signal thread to die. We use this mainly to
00248       avoid getting warnings that internal::my_thread_end has not been called
00249     */
00250     bool completed= false;
00251     /*
00252      * We send SIGTERM and then do a timed join. If that fails we will on
00253      * the last pthread_kill() call SIGTSTP. OSX (and FreeBSD) seem to
00254      * prefer this. -Brian
00255    */
00256     uint32_t count= 2; // How many times to try join and see if the caller died.
00257     while (not completed and count--)
00258     {
00259       int signal= count == 1 ? SIGTSTP : SIGTERM;
00260       
00261       if (int error= pthread_kill(thread.native_handle(), signal))
00262       {
00263         char buffer[1024]; // No reason for number;
00264         strerror_r(error, buffer, sizeof(buffer));
00265         std::cerr << "pthread_kill() error on shutdown of signal thread (" << buffer << ")\n";
00266         break;
00267       }
00268       else
00269       {
00270         boost::posix_time::milliseconds duration(100);
00271         completed= thread.timed_join(duration);
00272       }
00273     }
00274   }
00275 };
00276 
00277 static int init(drizzled::module::Context& context)
00278 {
00279   context.add(new SignalHandler);
00280 
00281   return 0;
00282 }
00283 
00284 
00285 DRIZZLE_DECLARE_PLUGIN
00286 {
00287   DRIZZLE_VERSION_ID,
00288   "signal_handler",
00289   "0.1",
00290   "Brian Aker",
00291   "Default Signal Handler",
00292   PLUGIN_LICENSE_GPL,
00293   init, /* Plugin Init */
00294   NULL,   /* depends */
00295   NULL    /* config options */
00296 }
00297 DRIZZLE_DECLARE_PLUGIN_END;