1 /*- 2 * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Spectra Logic Corporation 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions, and the following disclaimer, 10 * without modification. 11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12 * substantially similar to the "NO WARRANTY" disclaimer below 13 * ("Disclaimer") and any redistribution must be conditioned upon 14 * including a substantially similar Disclaimer requirement for further 15 * binary redistribution. 16 * 17 * NO WARRANTY 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 27 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGES. 29 * 30 * Authors: Justin T. Gibbs (Spectra Logic Corporation) 31 */ 32 33 /** 34 * \file zfsd.cc 35 * 36 * The ZFS daemon consumes kernel devdctl(4) event data via devd(8)'s 37 * unix domain socket in order to react to system changes that impact 38 * the function of ZFS storage pools. The goal of this daemon is to 39 * provide similar functionality to the Solaris ZFS Diagnostic Engine 40 * (zfs-diagnosis), the Solaris ZFS fault handler (zfs-retire), and 41 * the Solaris ZFS vdev insertion agent (zfs-mod sysevent handler). 42 */ 43 44 #include <sys/cdefs.h> 45 #include <sys/param.h> 46 #include <sys/fs/zfs.h> 47 48 #include <err.h> 49 #include <libgeom.h> 50 #include <libutil.h> 51 #include <poll.h> 52 #include <syslog.h> 53 54 #include <libzfs.h> 55 56 #include <list> 57 #include <map> 58 #include <string> 59 60 #include <devdctl/guid.h> 61 #include <devdctl/event.h> 62 #include <devdctl/event_factory.h> 63 #include <devdctl/exception.h> 64 #include <devdctl/consumer.h> 65 66 #include "callout.h" 67 #include "vdev_iterator.h" 68 #include "zfsd_event.h" 69 #include "case_file.h" 70 #include "vdev.h" 71 #include "vdev_iterator.h" 72 #include "zfsd.h" 73 #include "zfsd_exception.h" 74 #include "zpool_list.h" 75 76 __FBSDID("$FreeBSD$"); 77 78 /*================================== Macros ==================================*/ 79 #define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x)) 80 81 /*============================ Namespace Control =============================*/ 82 using DevdCtl::Event; 83 using DevdCtl::EventFactory; 84 using DevdCtl::EventList; 85 86 /*================================ Global Data ===============================*/ 87 int g_debug = 0; 88 libzfs_handle_t *g_zfsHandle; 89 90 /*--------------------------------- ZfsDaemon --------------------------------*/ 91 //- ZfsDaemon Static Private Data ---------------------------------------------- 92 ZfsDaemon *ZfsDaemon::s_theZfsDaemon; 93 bool ZfsDaemon::s_logCaseFiles; 94 bool ZfsDaemon::s_terminateEventLoop; 95 char ZfsDaemon::s_pidFilePath[] = "/var/run/zfsd.pid"; 96 pidfh *ZfsDaemon::s_pidFH; 97 int ZfsDaemon::s_signalPipeFD[2]; 98 bool ZfsDaemon::s_systemRescanRequested(false); 99 EventFactory::Record ZfsDaemon::s_registryEntries[] = 100 { 101 { Event::NOTIFY, "DEVFS", &DevfsEvent::Builder }, 102 { Event::NOTIFY, "GEOM", &GeomEvent::Builder }, 103 { Event::NOTIFY, "ZFS", &ZfsEvent::Builder } 104 }; 105 106 //- ZfsDaemon Static Public Methods -------------------------------------------- 107 ZfsDaemon & 108 ZfsDaemon::Get() 109 { 110 return (*s_theZfsDaemon); 111 } 112 113 void 114 ZfsDaemon::WakeEventLoop() 115 { 116 write(s_signalPipeFD[1], "+", 1); 117 } 118 119 void 120 ZfsDaemon::RequestSystemRescan() 121 { 122 s_systemRescanRequested = true; 123 ZfsDaemon::WakeEventLoop(); 124 } 125 126 void 127 ZfsDaemon::Run() 128 { 129 ZfsDaemon daemon; 130 131 while (s_terminateEventLoop == false) { 132 133 try { 134 daemon.DisconnectFromDevd(); 135 136 if (daemon.ConnectToDevd() == false) { 137 sleep(30); 138 continue; 139 } 140 141 daemon.DetectMissedEvents(); 142 143 daemon.EventLoop(); 144 145 } catch (const DevdCtl::Exception &exp) { 146 exp.Log(); 147 } 148 } 149 150 daemon.DisconnectFromDevd(); 151 } 152 153 //- ZfsDaemon Private Methods -------------------------------------------------- 154 ZfsDaemon::ZfsDaemon() 155 : Consumer(/*defBuilder*/NULL, s_registryEntries, 156 NUM_ELEMENTS(s_registryEntries)) 157 { 158 if (s_theZfsDaemon != NULL) 159 errx(1, "Multiple ZfsDaemon instances created. Exiting"); 160 161 s_theZfsDaemon = this; 162 163 if (pipe(s_signalPipeFD) != 0) 164 errx(1, "Unable to allocate signal pipe. Exiting"); 165 166 if (fcntl(s_signalPipeFD[0], F_SETFL, O_NONBLOCK) == -1) 167 errx(1, "Unable to set pipe as non-blocking. Exiting"); 168 169 if (fcntl(s_signalPipeFD[1], F_SETFL, O_NONBLOCK) == -1) 170 errx(1, "Unable to set pipe as non-blocking. Exiting"); 171 172 signal(SIGHUP, ZfsDaemon::RescanSignalHandler); 173 signal(SIGINFO, ZfsDaemon::InfoSignalHandler); 174 signal(SIGINT, ZfsDaemon::QuitSignalHandler); 175 signal(SIGTERM, ZfsDaemon::QuitSignalHandler); 176 signal(SIGUSR1, ZfsDaemon::RescanSignalHandler); 177 178 g_zfsHandle = libzfs_init(); 179 if (g_zfsHandle == NULL) 180 errx(1, "Unable to initialize ZFS library. Exiting"); 181 182 Callout::Init(); 183 InitializeSyslog(); 184 OpenPIDFile(); 185 186 if (g_debug == 0) 187 daemon(0, 0); 188 189 UpdatePIDFile(); 190 } 191 192 ZfsDaemon::~ZfsDaemon() 193 { 194 PurgeCaseFiles(); 195 ClosePIDFile(); 196 } 197 198 void 199 ZfsDaemon::PurgeCaseFiles() 200 { 201 CaseFile::PurgeAll(); 202 } 203 204 bool 205 ZfsDaemon::VdevAddCaseFile(Vdev &vdev, void *cbArg) 206 { 207 if (vdev.State() != VDEV_STATE_HEALTHY) 208 CaseFile::Create(vdev); 209 210 return (/*break early*/false); 211 } 212 213 void 214 ZfsDaemon::BuildCaseFiles() 215 { 216 ZpoolList zpl; 217 ZpoolList::iterator pool; 218 219 /* Add CaseFiles for vdevs with issues. */ 220 for (pool = zpl.begin(); pool != zpl.end(); pool++) 221 VdevIterator(*pool).Each(VdevAddCaseFile, NULL); 222 223 /* De-serialize any saved cases. */ 224 CaseFile::DeSerialize(); 225 226 /* Simulate config_sync events to force CaseFile reevaluation */ 227 for (pool = zpl.begin(); pool != zpl.end(); pool++) { 228 char evString[160]; 229 Event *event; 230 nvlist_t *config; 231 uint64_t poolGUID; 232 const char *poolname; 233 234 poolname = zpool_get_name(*pool); 235 config = zpool_get_config(*pool, NULL); 236 if (config == NULL) { 237 syslog(LOG_ERR, "ZFSDaemon::BuildCaseFiles: Could not " 238 "find pool config for pool %s", poolname); 239 continue; 240 } 241 if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, 242 &poolGUID) != 0) { 243 syslog(LOG_ERR, "ZFSDaemon::BuildCaseFiles: Could not " 244 "find pool guid for pool %s", poolname); 245 continue; 246 } 247 248 249 snprintf(evString, 160, "!system=ZFS subsystem=ZFS " 250 "type=misc.fs.zfs.config_sync sub_type=synthesized " 251 "pool_name=%s pool_guid=%" PRIu64 "\n", poolname, poolGUID); 252 event = Event::CreateEvent(GetFactory(), string(evString)); 253 if (event != NULL) { 254 event->Process(); 255 delete event; 256 } 257 } 258 } 259 260 void 261 ZfsDaemon::RescanSystem() 262 { 263 struct gmesh mesh; 264 struct gclass *mp; 265 struct ggeom *gp; 266 struct gprovider *pp; 267 int result; 268 269 /* 270 * The devdctl system doesn't replay events for new consumers 271 * of the interface. Emit manufactured DEVFS arrival events 272 * for any devices that already before we started or during 273 * periods where we've lost our connection to devd. 274 */ 275 result = geom_gettree(&mesh); 276 if (result != 0) { 277 syslog(LOG_ERR, "ZfsDaemon::RescanSystem: " 278 "geom_gettree faild with error %d\n", result); 279 return; 280 } 281 282 const string evStart("!system=DEVFS subsystem=CDEV type=CREATE " 283 "sub_type=synthesized cdev="); 284 LIST_FOREACH(mp, &mesh.lg_class, lg_class) { 285 LIST_FOREACH(gp, &mp->lg_geom, lg_geom) { 286 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 287 Event *event; 288 289 string evString(evStart + pp->lg_name + "\n"); 290 event = Event::CreateEvent(GetFactory(), 291 evString); 292 if (event != NULL) { 293 if (event->Process()) 294 SaveEvent(*event); 295 delete event; 296 } 297 } 298 } 299 } 300 geom_deletetree(&mesh); 301 } 302 303 void 304 ZfsDaemon::DetectMissedEvents() 305 { 306 do { 307 PurgeCaseFiles(); 308 309 /* 310 * Discard any events waiting for us. We don't know 311 * if they still apply to the current state of the 312 * system. 313 */ 314 FlushEvents(); 315 316 BuildCaseFiles(); 317 318 /* 319 * If the system state has changed during our 320 * interrogation, start over. 321 */ 322 } while (s_terminateEventLoop == false && EventsPending()); 323 324 RescanSystem(); 325 } 326 327 void 328 ZfsDaemon::EventLoop() 329 { 330 while (s_terminateEventLoop == false) { 331 struct pollfd fds[2]; 332 int result; 333 334 if (s_logCaseFiles == true) { 335 EventList::iterator event(m_unconsumedEvents.begin()); 336 s_logCaseFiles = false; 337 CaseFile::LogAll(); 338 while (event != m_unconsumedEvents.end()) 339 (*event++)->Log(LOG_INFO); 340 } 341 342 Callout::ExpireCallouts(); 343 344 /* Wait for data. */ 345 fds[0].fd = m_devdSockFD; 346 fds[0].events = POLLIN; 347 fds[0].revents = 0; 348 fds[1].fd = s_signalPipeFD[0]; 349 fds[1].events = POLLIN; 350 fds[1].revents = 0; 351 result = poll(fds, NUM_ELEMENTS(fds), /*timeout*/INFTIM); 352 if (result == -1) { 353 if (errno == EINTR) 354 continue; 355 else 356 err(1, "Polling for devd events failed"); 357 } else if (result == 0) { 358 errx(1, "Unexpected result of 0 from poll. Exiting"); 359 } 360 361 if ((fds[0].revents & POLLIN) != 0) 362 ProcessEvents(); 363 364 if ((fds[1].revents & POLLIN) != 0) { 365 static char discardBuf[128]; 366 367 /* 368 * This pipe exists just to close the signal 369 * race. Its contents are of no interest to 370 * us, but we must ensure that future signals 371 * have space in the pipe to write. 372 */ 373 while (read(s_signalPipeFD[0], discardBuf, 374 sizeof(discardBuf)) > 0) 375 ; 376 } 377 378 if (s_systemRescanRequested == true) { 379 s_systemRescanRequested = false; 380 syslog(LOG_INFO, "System Rescan request processed."); 381 RescanSystem(); 382 } 383 384 if ((fds[0].revents & POLLERR) != 0) { 385 syslog(LOG_INFO, "POLLERROR detected on devd socket."); 386 break; 387 } 388 389 if ((fds[0].revents & POLLHUP) != 0) { 390 syslog(LOG_INFO, "POLLHUP detected on devd socket."); 391 break; 392 } 393 } 394 } 395 //- ZfsDaemon staic Private Methods -------------------------------------------- 396 void 397 ZfsDaemon::InfoSignalHandler(int) 398 { 399 s_logCaseFiles = true; 400 ZfsDaemon::WakeEventLoop(); 401 } 402 403 void 404 ZfsDaemon::RescanSignalHandler(int) 405 { 406 RequestSystemRescan(); 407 } 408 409 void 410 ZfsDaemon::QuitSignalHandler(int) 411 { 412 s_terminateEventLoop = true; 413 ZfsDaemon::WakeEventLoop(); 414 } 415 416 void 417 ZfsDaemon::OpenPIDFile() 418 { 419 pid_t otherPID; 420 421 s_pidFH = pidfile_open(s_pidFilePath, 0600, &otherPID); 422 if (s_pidFH == NULL) { 423 if (errno == EEXIST) 424 errx(1, "already running as PID %d. Exiting", otherPID); 425 warn("cannot open PID file"); 426 } 427 } 428 429 void 430 ZfsDaemon::UpdatePIDFile() 431 { 432 if (s_pidFH != NULL) 433 pidfile_write(s_pidFH); 434 } 435 436 void 437 ZfsDaemon::ClosePIDFile() 438 { 439 if (s_pidFH != NULL) 440 pidfile_close(s_pidFH); 441 } 442 443 void 444 ZfsDaemon::InitializeSyslog() 445 { 446 openlog("zfsd", LOG_NDELAY, LOG_DAEMON); 447 } 448 449