[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [PATCH v4 19/42] remote: in per-driver daemons ensure that state initialize succeeds



When running in libvirtd, we are happy for any of the drivers to simply
skip their initialization in virStateInitialize, as other drivers are
still potentially useful.

When running in per-driver daemons though, we want the daemon to abort
startup if the driver cannot initialize itself, as the daemon will be
useless without it.

Reviewed-by: Andrea Bolognani <abologna redhat com>
Signed-off-by: Daniel P. Berrangé <berrange redhat com>
---
 src/bhyve/bhyve_driver.c                | 10 +++++-----
 src/driver-state.h                      |  8 +++++++-
 src/interface/interface_backend_netcf.c |  8 ++++----
 src/interface/interface_backend_udev.c  |  4 ++--
 src/libvirt.c                           | 15 ++++++++++++---
 src/libvirt_internal.h                  |  1 +
 src/libxl/libxl_driver.c                | 10 +++++-----
 src/lxc/lxc_driver.c                    | 12 ++++++------
 src/network/bridge_driver.c             |  4 ++--
 src/node_device/node_device_hal.c       | 12 ++++++------
 src/node_device/node_device_udev.c      |  8 ++++----
 src/nwfilter/nwfilter_driver.c          | 12 ++++++------
 src/qemu/qemu_driver.c                  |  8 ++++----
 src/remote/remote_daemon.c              |  6 ++++++
 src/remote/remote_driver.c              |  2 +-
 src/secret/secret_driver.c              |  8 ++++----
 src/storage/storage_driver.c            |  8 ++++----
 src/vz/vz_driver.c                      | 14 +++++++-------
 18 files changed, 86 insertions(+), 64 deletions(-)

diff --git a/src/bhyve/bhyve_driver.c b/src/bhyve/bhyve_driver.c
index 5387ac5570..e2c1b00080 100644
--- a/src/bhyve/bhyve_driver.c
+++ b/src/bhyve/bhyve_driver.c
@@ -1220,16 +1220,16 @@ bhyveStateInitialize(bool privileged,
 {
     if (!privileged) {
         VIR_INFO("Not running privileged, disabling driver");
-        return 0;
+        return VIR_DRV_STATE_INIT_SKIPPED;
     }
 
     if (VIR_ALLOC(bhyve_driver) < 0)
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
 
     bhyve_driver->lockFD = -1;
     if (virMutexInit(&bhyve_driver->lock) < 0) {
         VIR_FREE(bhyve_driver);
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
     }
 
     if (!(bhyve_driver->closeCallbacks = virCloseCallbacksNew()))
@@ -1303,11 +1303,11 @@ bhyveStateInitialize(bool privileged,
 
     bhyveAutostartDomains(bhyve_driver);
 
-    return 0;
+    return VIR_DRV_STATE_INIT_COMPLETE;
 
  cleanup:
     bhyveStateCleanup();
-    return -1;
+    return VIR_DRV_STATE_INIT_ERROR;
 }
 
 unsigned
diff --git a/src/driver-state.h b/src/driver-state.h
index 974b2252ee..69e2678dfc 100644
--- a/src/driver-state.h
+++ b/src/driver-state.h
@@ -24,7 +24,13 @@
 # error "Don't include this file directly, only use driver.h"
 #endif
 
-typedef int
+typedef enum {
+    VIR_DRV_STATE_INIT_ERROR = -1,
+    VIR_DRV_STATE_INIT_SKIPPED,
+    VIR_DRV_STATE_INIT_COMPLETE,
+} virDrvStateInitResult;
+
+typedef virDrvStateInitResult
 (*virDrvStateInitialize)(bool privileged,
                          virStateInhibitCallback callback,
                          void *opaque);
diff --git a/src/interface/interface_backend_netcf.c b/src/interface/interface_backend_netcf.c
index 0000587cee..eb509ccc13 100644
--- a/src/interface/interface_backend_netcf.c
+++ b/src/interface/interface_backend_netcf.c
@@ -93,10 +93,10 @@ netcfStateInitialize(bool privileged,
                      void *opaque ATTRIBUTE_UNUSED)
 {
     if (virNetcfDriverStateInitialize() < 0)
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
 
     if (!(driver = virObjectLockableNew(virNetcfDriverStateClass)))
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
 
     driver->privileged = privileged;
 
@@ -129,12 +129,12 @@ netcfStateInitialize(bool privileged,
                        _("failed to initialize netcf"));
         goto error;
     }
-    return 0;
+    return VIR_DRV_STATE_INIT_COMPLETE;
 
  error:
     virObjectUnref(driver);
     driver = NULL;
-    return -1;
+    return VIR_DRV_STATE_INIT_ERROR;
 }
 
 
diff --git a/src/interface/interface_backend_udev.c b/src/interface/interface_backend_udev.c
index fea5108dbc..ef748540d1 100644
--- a/src/interface/interface_backend_udev.c
+++ b/src/interface/interface_backend_udev.c
@@ -1172,7 +1172,7 @@ udevStateInitialize(bool privileged,
                     virStateInhibitCallback callback ATTRIBUTE_UNUSED,
                     void *opaque ATTRIBUTE_UNUSED)
 {
-    int ret = -1;
+    int ret = VIR_DRV_STATE_INIT_ERROR;
 
     if (VIR_ALLOC(driver) < 0)
         goto cleanup;
@@ -1210,7 +1210,7 @@ udevStateInitialize(bool privileged,
     }
     driver->privileged = privileged;
 
-    ret = 0;
+    ret = VIR_DRV_STATE_INIT_COMPLETE;
 
  cleanup:
     if (ret < 0)
diff --git a/src/libvirt.c b/src/libvirt.c
index 768ad348c7..906bab8128 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -622,6 +622,7 @@ virRegisterStateDriver(virStateDriverPtr driver)
 /**
  * virStateInitialize:
  * @privileged: set to true if running with root privilege, false otherwise
+ * @mandatory: set to true if all drivers must report success, not skipped
  * @callback: callback to invoke to inhibit shutdown of the daemon
  * @opaque: data to pass to @callback
  *
@@ -631,6 +632,7 @@ virRegisterStateDriver(virStateDriverPtr driver)
  */
 int
 virStateInitialize(bool privileged,
+                   bool mandatory,
                    virStateInhibitCallback callback,
                    void *opaque)
 {
@@ -641,15 +643,22 @@ virStateInitialize(bool privileged,
 
     for (i = 0; i < virStateDriverTabCount; i++) {
         if (virStateDriverTab[i]->stateInitialize) {
+            virDrvStateInitResult ret;
             VIR_DEBUG("Running global init for %s state driver",
                       virStateDriverTab[i]->name);
-            if (virStateDriverTab[i]->stateInitialize(privileged,
-                                                      callback,
-                                                      opaque) < 0) {
+            ret = virStateDriverTab[i]->stateInitialize(privileged,
+                                                        callback,
+                                                        opaque);
+            VIR_DEBUG("State init result %d (mandatory=%d)", ret, mandatory);
+            if (ret == VIR_DRV_STATE_INIT_ERROR) {
                 VIR_ERROR(_("Initialization of %s state driver failed: %s"),
                           virStateDriverTab[i]->name,
                           virGetLastErrorMessage());
                 return -1;
+            } else if (ret == VIR_DRV_STATE_INIT_SKIPPED && mandatory) {
+                VIR_ERROR(_("Initialization of mandatory %s state driver skipped"),
+                          virStateDriverTab[i]->name);
+                return -1;
             }
         }
     }
diff --git a/src/libvirt_internal.h b/src/libvirt_internal.h
index 3f012fdd4b..4a74dbc2af 100644
--- a/src/libvirt_internal.h
+++ b/src/libvirt_internal.h
@@ -30,6 +30,7 @@ typedef void (*virStateInhibitCallback)(bool inhibit,
                                         void *opaque);
 
 int virStateInitialize(bool privileged,
+                       bool mandatory,
                        virStateInhibitCallback inhibit,
                        void *opaque);
 int virStateCleanup(void);
diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c
index f430522b35..c85bc3f5d0 100644
--- a/src/libxl/libxl_driver.c
+++ b/src/libxl/libxl_driver.c
@@ -657,17 +657,17 @@ libxlStateInitialize(bool privileged,
     char ebuf[1024];
 
     if (!libxlDriverShouldLoad(privileged))
-        return 0;
+        return VIR_DRV_STATE_INIT_SKIPPED;
 
     if (VIR_ALLOC(libxl_driver) < 0)
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
 
     libxl_driver->lockFD = -1;
     if (virMutexInit(&libxl_driver->lock) < 0) {
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        "%s", _("cannot initialize mutex"));
         VIR_FREE(libxl_driver);
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
     }
 
     /* Allocate bitmap for vnc port reservation */
@@ -806,12 +806,12 @@ libxlStateInitialize(bool privileged,
     virDomainObjListForEach(libxl_driver->domains, libxlDomainManagedSaveLoad,
                             libxl_driver);
 
-    return 0;
+    return VIR_DRV_STATE_INIT_COMPLETE;
 
  error:
     VIR_FREE(driverConf);
     libxlStateCleanup();
-    return -1;
+    return VIR_DRV_STATE_INIT_ERROR;
 }
 
 static int
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c
index d0b6703101..0baf18f3ef 100644
--- a/src/lxc/lxc_driver.c
+++ b/src/lxc/lxc_driver.c
@@ -1545,7 +1545,7 @@ static int lxcStateInitialize(bool privileged,
     /* Check that the user is root, silently disable if not */
     if (!privileged) {
         VIR_INFO("Not running privileged, disabling driver");
-        return 0;
+        return VIR_DRV_STATE_INIT_SKIPPED;
     }
 
     /* Check that this is a container enabled kernel */
@@ -1554,15 +1554,15 @@ static int lxcStateInitialize(bool privileged,
                                      VIR_PROCESS_NAMESPACE_UTS |
                                      VIR_PROCESS_NAMESPACE_IPC) < 0) {
         VIR_INFO("LXC support not available in this kernel, disabling driver");
-        return 0;
+        return VIR_DRV_STATE_INIT_SKIPPED;
     }
 
     if (VIR_ALLOC(lxc_driver) < 0)
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
     lxc_driver->lockFD = -1;
     if (virMutexInit(&lxc_driver->lock) < 0) {
         VIR_FREE(lxc_driver);
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
     }
 
     if (!(lxc_driver->domains = virDomainObjListNew()))
@@ -1633,12 +1633,12 @@ static int lxcStateInitialize(bool privileged,
     virLXCProcessAutostartAll(lxc_driver);
 
     virObjectUnref(caps);
-    return 0;
+    return VIR_DRV_STATE_INIT_COMPLETE;
 
  cleanup:
     virObjectUnref(caps);
     lxcStateCleanup();
-    return -1;
+    return VIR_DRV_STATE_INIT_ERROR;
 }
 
 static void lxcNotifyLoadDomain(virDomainObjPtr vm, int newVM, void *opaque)
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 41fa89a4af..2b1fa59390 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -713,7 +713,7 @@ networkStateInitialize(bool privileged,
                        virStateInhibitCallback callback ATTRIBUTE_UNUSED,
                        void *opaque ATTRIBUTE_UNUSED)
 {
-    int ret = -1;
+    int ret = VIR_DRV_STATE_INIT_ERROR;
     char *configdir = NULL;
     char *rundir = NULL;
 #ifdef WITH_FIREWALLD
@@ -847,7 +847,7 @@ networkStateInitialize(bool privileged,
     }
 #endif
 
-    ret = 0;
+    ret = VIR_DRV_STATE_INIT_COMPLETE;
  cleanup:
     VIR_FREE(configdir);
     VIR_FREE(rundir);
diff --git a/src/node_device/node_device_hal.c b/src/node_device/node_device_hal.c
index 1f3f867599..d46e4e98f3 100644
--- a/src/node_device/node_device_hal.c
+++ b/src/node_device/node_device_hal.c
@@ -599,7 +599,7 @@ nodeStateInitialize(bool privileged ATTRIBUTE_UNUSED,
     char **udi = NULL;
     int num_devs;
     size_t i;
-    int ret = -1;
+    int ret = VIR_DRV_STATE_INIT_ERROR;
     DBusConnection *sysbus;
     DBusError err;
 
@@ -608,12 +608,12 @@ nodeStateInitialize(bool privileged ATTRIBUTE_UNUSED,
           cmpstringp);
 
     if (VIR_ALLOC(driver) < 0)
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
 
     driver->lockFD = -1;
     if (virMutexInit(&driver->lock) < 0) {
         VIR_FREE(driver);
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
     }
     nodeDeviceLock();
 
@@ -648,7 +648,7 @@ nodeStateInitialize(bool privileged ATTRIBUTE_UNUSED,
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("DBus not available, disabling HAL driver: %s"),
                        virGetLastErrorMessage());
-        ret = 0;
+        ret = VIR_DRV_STATE_INIT_SKIPPED;
         goto failure;
     }
 
@@ -671,7 +671,7 @@ nodeStateInitialize(bool privileged ATTRIBUTE_UNUSED,
         /* We don't want to show a fatal error here,
            otherwise entire libvirtd shuts down when
            hald isn't running */
-        ret = 0;
+        ret = VIR_DRV_STATE_INIT_SKIPPED;
         goto failure;
     }
 
@@ -709,7 +709,7 @@ nodeStateInitialize(bool privileged ATTRIBUTE_UNUSED,
     }
     VIR_FREE(udi);
 
-    return 0;
+    return VIR_DRV_STATE_INIT_COMPLETE;
 
  failure:
     if (dbus_error_is_set(&err)) {
diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c
index 8bc63c506c..adf60e4537 100644
--- a/src/node_device/node_device_udev.c
+++ b/src/node_device/node_device_udev.c
@@ -1816,14 +1816,14 @@ nodeStateInitialize(bool privileged,
     virThread enumThread;
 
     if (VIR_ALLOC(driver) < 0)
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
 
     driver->lockFD = -1;
     if (virMutexInit(&driver->lock) < 0) {
         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                        _("Unable to initialize mutex"));
         VIR_FREE(driver);
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
     }
 
     driver->privileged = privileged;
@@ -1919,11 +1919,11 @@ nodeStateInitialize(bool privileged,
         goto cleanup;
     }
 
-    return 0;
+    return VIR_DRV_STATE_INIT_COMPLETE;
 
  cleanup:
     nodeStateCleanup();
-    return -1;
+    return VIR_DRV_STATE_INIT_ERROR;
 
  unlock:
     virObjectUnlock(priv);
diff --git a/src/nwfilter/nwfilter_driver.c b/src/nwfilter/nwfilter_driver.c
index 530e4f5872..6073143437 100644
--- a/src/nwfilter/nwfilter_driver.c
+++ b/src/nwfilter/nwfilter_driver.c
@@ -184,10 +184,10 @@ nwfilterStateInitialize(bool privileged,
 
     if (virDBusHasSystemBus() &&
         !(sysbus = virDBusGetSystemBus()))
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
 
     if (VIR_ALLOC(driver) < 0)
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
 
     driver->lockFD = -1;
     if (virMutexInit(&driver->lock) < 0)
@@ -201,7 +201,7 @@ nwfilterStateInitialize(bool privileged,
         goto error;
 
     if (!privileged)
-        return 0;
+        return VIR_DRV_STATE_INIT_SKIPPED;
 
     nwfilterDriverLock();
 
@@ -281,13 +281,13 @@ nwfilterStateInitialize(bool privileged,
 
     nwfilterDriverUnlock();
 
-    return 0;
+    return VIR_DRV_STATE_INIT_COMPLETE;
 
  error:
     nwfilterDriverUnlock();
     nwfilterStateCleanup();
 
-    return -1;
+    return VIR_DRV_STATE_INIT_ERROR;
 
  err_techdrivers_shutdown:
     virNWFilterTechDriversShutdown();
@@ -302,7 +302,7 @@ nwfilterStateInitialize(bool privileged,
     virNWFilterObjListFree(driver->nwfilters);
     VIR_FREE(driver);
 
-    return -1;
+    return VIR_DRV_STATE_INIT_ERROR;
 }
 
 /**
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index ff83d1c024..e0bf8ea2c4 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -730,7 +730,7 @@ qemuStateInitialize(bool privileged,
     size_t i;
 
     if (VIR_ALLOC(qemu_driver) < 0)
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
 
     qemu_driver->lockFD = -1;
 
@@ -738,7 +738,7 @@ qemuStateInitialize(bool privileged,
         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                        _("cannot initialize mutex"));
         VIR_FREE(qemu_driver);
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
     }
 
     qemu_driver->inhibitCallback = callback;
@@ -1074,14 +1074,14 @@ qemuStateInitialize(bool privileged,
 
     qemuAutostartDomains(qemu_driver);
 
-    return 0;
+    return VIR_DRV_STATE_INIT_COMPLETE;
 
  error:
     VIR_FREE(driverConf);
     VIR_FREE(hugepagePath);
     VIR_FREE(memoryBackingPath);
     qemuStateCleanup();
-    return -1;
+    return VIR_DRV_STATE_INIT_ERROR;
 }
 
 static void qemuNotifyLoadDomain(virDomainObjPtr vm, int newVM, void *opaque)
diff --git a/src/remote/remote_daemon.c b/src/remote/remote_daemon.c
index d63f2daac1..ad3c163120 100644
--- a/src/remote/remote_daemon.c
+++ b/src/remote/remote_daemon.c
@@ -792,6 +792,11 @@ static void daemonRunStateInit(void *opaque)
 {
     virNetDaemonPtr dmn = opaque;
     virIdentityPtr sysident = virIdentityGetSystem();
+#ifdef MODULE_NAME
+    bool mandatory = true;
+#else /* ! MODULE_NAME */
+    bool mandatory = false;
+#endif /* ! MODULE_NAME */
 
     virIdentitySetCurrent(sysident);
 
@@ -804,6 +809,7 @@ static void daemonRunStateInit(void *opaque)
      * we're ready, since it can take a long time and this will
      * seriously delay OS bootup process */
     if (virStateInitialize(virNetDaemonIsPrivileged(dmn),
+                           mandatory,
                            daemonInhibitCallback,
                            dmn) < 0) {
         VIR_ERROR(_("Driver state initialization failed"));
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index 76ea49ed8e..ef240052f1 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -195,7 +195,7 @@ remoteStateInitialize(bool privileged ATTRIBUTE_UNUSED,
      * re-entering ourselves
      */
     inside_daemon = true;
-    return 0;
+    return VIR_DRV_STATE_INIT_COMPLETE;
 }
 
 
diff --git a/src/secret/secret_driver.c b/src/secret/secret_driver.c
index 0af2bcef96..0d5ea05f56 100644
--- a/src/secret/secret_driver.c
+++ b/src/secret/secret_driver.c
@@ -457,12 +457,12 @@ secretStateInitialize(bool privileged,
                       void *opaque ATTRIBUTE_UNUSED)
 {
     if (VIR_ALLOC(driver) < 0)
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
 
     driver->lockFD = -1;
     if (virMutexInit(&driver->lock) < 0) {
         VIR_FREE(driver);
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
     }
     secretDriverLock();
 
@@ -514,12 +514,12 @@ secretStateInitialize(bool privileged,
         goto error;
 
     secretDriverUnlock();
-    return 0;
+    return VIR_DRV_STATE_INIT_COMPLETE;
 
  error:
     secretDriverUnlock();
     secretStateCleanup();
-    return -1;
+    return VIR_DRV_STATE_INIT_ERROR;
 }
 
 
diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c
index 03ac6a6845..dfa654178b 100644
--- a/src/storage/storage_driver.c
+++ b/src/storage/storage_driver.c
@@ -255,12 +255,12 @@ storageStateInitialize(bool privileged,
     VIR_AUTOFREE(char *) rundir = NULL;
 
     if (VIR_ALLOC(driver) < 0)
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
 
     driver->lockFD = -1;
     if (virMutexInit(&driver->lock) < 0) {
         VIR_FREE(driver);
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
     }
     storageDriverLock();
 
@@ -326,12 +326,12 @@ storageStateInitialize(bool privileged,
 
     storageDriverUnlock();
 
-    return 0;
+    return VIR_DRV_STATE_INIT_COMPLETE;
 
  error:
     storageDriverUnlock();
     storageStateCleanup();
-    return -1;
+    return VIR_DRV_STATE_INIT_ERROR;
 }
 
 /**
diff --git a/src/vz/vz_driver.c b/src/vz/vz_driver.c
index f5d05a7f43..da72b209d1 100644
--- a/src/vz/vz_driver.c
+++ b/src/vz/vz_driver.c
@@ -4118,36 +4118,36 @@ vzStateInitialize(bool privileged,
                   void *opaque ATTRIBUTE_UNUSED)
 {
     if (!privileged)
-        return 0;
+        return VIR_DRV_STATE_INIT_SKIPPED;
 
     vz_driver_privileged = privileged;
 
     if (virFileMakePathWithMode(VZ_STATEDIR, S_IRWXU) < 0) {
         virReportSystemError(errno, _("cannot create state directory '%s'"),
                              VZ_STATEDIR);
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
     }
 
     if ((vz_driver_lock_fd =
          virPidFileAcquire(VZ_STATEDIR, "driver", false, getpid())) < 0)
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
 
     if (prlsdkInit() < 0) {
         VIR_DEBUG("%s", _("Can't initialize Parallels SDK"));
-        return -1;
+        return VIR_DRV_STATE_INIT_ERROR;
     }
 
-   if (virMutexInit(&vz_driver_lock) < 0)
+    if (virMutexInit(&vz_driver_lock) < 0)
         goto error;
 
     /* Failing to create driver here is not fatal and only means
      * that next driver client will try once more when connecting */
     vz_driver = vzDriverObjNew();
-    return 0;
+    return VIR_DRV_STATE_INIT_COMPLETE;
 
  error:
     vzStateCleanup();
-    return -1;
+    return VIR_DRV_STATE_INIT_ERROR;
 }
 
 static virStateDriver vzStateDriver = {
-- 
2.21.0


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]