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