[patch] fstab-sync gets to write to /etc/auto.hal

Luke Kenneth Casson Leighton lkcl at lkcl.net
Wed Sep 22 15:01:33 PDT 2004


ha ha.  's'veryfunny.  makes HAL go up-and-down like a yoyo with
automount going "oh yes, here _i'll_ mount it for you, and here's
it gone again..."

so what does HAL do?  it goes "oo, mtab's changed again like it did
4 seconds ago _let's_ tell the user _all_ about it".

stage 2 will be an option to _disable_ the mount and umount stuff
in HAL (based on a config opt in /etc/hal/hald.conf).

not sure whether to do a stage 3 which is to remove the entry from
/etc/auto.hal [because i think if i do that it causes automount to
react a bit funny: the kernel doesn't bother to send notify messages
back to /usr/sbin/automount if a mountpoint is missing from the
config file and a user tries to access it]

in combination with using fusexmp - the example code from the file
system user space - i believe this will solve once and for all the
"users removing drives" issue.

no, despite kernel 2.6 reputedly being capable of having USB drives
nicked out from under it, it _doesn't_ work.

l.

-- 
--
Truth, honesty and respect are rare commodities that all spring from
the same well: Love.  If you love yourself and everyone and everything
around you, funnily and coincidentally enough, life gets a lot better.
--
<a href="http://lkcl.net">      lkcl.net      </a> <br />
<a href="mailto:lkcl at lkcl.net"> lkcl at lkcl.net </a> <br />

-------------- next part --------------
--- ../../tmp/hal-0.2.97+cvs20040907/tools/fstab-sync.c	2004-09-01 17:47:06.000000000 +0100
+++ fstab-sync.c	2004-09-22 22:42:11.000000000 +0100
@@ -77,6 +76,15 @@
 #define TEMP_FSTAB_PREFIX ".fstab.hal."
 #define TEMP_FSTAB_MAX_LENGTH 64
 
+#ifndef AUTOFS_SYNC_MOUNT_ROOT
+#  define AUTOFS_SYNC_MOUNT_ROOT "/etc/auto.hal"
+#endif
+
+/* only used for non-automount fstab-sync management: for
+ * automount management, place an entry in /etc/auto.master:
+ * /media                 /etc/auto.hal --timeout 2
+ */
+
 #ifndef FSTAB_SYNC_MOUNT_ROOT
 #  define FSTAB_SYNC_MOUNT_ROOT "/media"
 #endif
@@ -127,9 +135,30 @@
   FS_TABLE_NUM_FIELD_TYPES
 } FSTableFieldType;
 
+static FSTableFieldType fstab_fields[] =
+{
+  FS_TABLE_FIELD_TYPE_BLOCK_DEVICE,
+  FS_TABLE_FIELD_TYPE_MOUNT_POINT,
+  FS_TABLE_FIELD_TYPE_FILE_SYSTEM_TYPE,
+  FS_TABLE_FIELD_TYPE_MOUNT_OPTIONS,
+  FS_TABLE_FIELD_TYPE_DUMP_FREQUENCY,
+  FS_TABLE_FIELD_TYPE_PASS_NUMBER,
+  FS_TABLE_FIELD_TYPE_WHITE_SPACE
+};
+
+static FSTableFieldType autofs_fields[] =
+{
+  FS_TABLE_FIELD_TYPE_MOUNT_POINT,
+  FS_TABLE_FIELD_TYPE_FILE_SYSTEM_TYPE,
+  FS_TABLE_FIELD_TYPE_MOUNT_OPTIONS,
+  FS_TABLE_FIELD_TYPE_BLOCK_DEVICE,
+  FS_TABLE_FIELD_TYPE_WHITE_SPACE
+};
+
 typedef struct FSTableField
 {
   FSTableFieldType type;
+  char *prefix;
   char *value;
   struct FSTableField *next;
 } FSTableField;
@@ -160,6 +189,12 @@
 static LibHalContext *hal_context = NULL;
 static pid_t pid;
 
+static boolean automount = FALSE;
+static char *fstab_path;
+static char extra_mount_opts[80];
+
+static FSTableFieldType *fs_fields;
+
 static void fs_table_line_add_field (FSTableLine *line, FSTableField *field);
 static boolean fs_table_line_is_generated (FSTableLine *line);
 static void fs_table_line_update_pointer (FSTableLine *line, FSTableField *field);
@@ -283,19 +318,26 @@
 #endif /* HAVE_SELINUX */
 
 static FSTableField *
-fs_table_field_new (FSTableFieldType type, const char *value)
+autofs_table_field_new (FSTableFieldType type, const char *value, const char *prefix)
 {
   FSTableField *field;
 
   field = malloc (sizeof (FSTableLine));
 
   field->type = type;
+  field->prefix = prefix ? strdup(prefix) : NULL;
   field->value = strdup (value);
   field->next = NULL;
 
   return field;
 }
 
+static FSTableField *
+fs_table_field_new (FSTableFieldType type, const char *value)
+{
+  return autofs_table_field_new(type, value, NULL);
+}
+
 static void
 fs_table_field_free (FSTableField *field)
 {
@@ -305,6 +347,11 @@
 
   field->type = FS_TABLE_FIELD_TYPE_WHITE_SPACE;
 
+  if (field->prefix != NULL)
+    {
+      free (field->prefix);
+      field->prefix = NULL;
+    }
   if (field->value != NULL)
     {
       free (field->value);
@@ -341,6 +388,44 @@
 }
 
 static FSTableLine *
+autofs_table_line_new_from_field_values (const char *block_device,
+                                     const char *mount_point,
+                                     const char *fs_type,
+                                     const char *mount_options,
+                                     int dump_frequency,
+                                     int pass_number)
+{
+  FSTableLine *line;
+  FSTableField *field;
+
+  line = fs_table_line_new ();
+
+  field = autofs_table_field_new (FS_TABLE_FIELD_TYPE_MOUNT_POINT, mount_point, NULL); 
+  fs_table_line_add_field (line, field);
+  
+  field = fs_table_field_new (FS_TABLE_FIELD_TYPE_WHITE_SPACE, 
+                              get_whitespace (mount_point, 24)); 
+  fs_table_line_add_field (line, field);
+
+  field = autofs_table_field_new (FS_TABLE_FIELD_TYPE_FILE_SYSTEM_TYPE, fs_type, "-fstype="); 
+  fs_table_line_add_field (line, field);
+
+  field = autofs_table_field_new (FS_TABLE_FIELD_TYPE_MOUNT_OPTIONS, mount_options,
+	          mount_options && mount_options[0] ? "," : NULL);
+  fs_table_line_add_field (line, field);
+
+  field = fs_table_field_new (FS_TABLE_FIELD_TYPE_WHITE_SPACE, 
+                              get_whitespace (mount_options, 24)); 
+  fs_table_line_add_field (line, field);
+
+  field = autofs_table_field_new (FS_TABLE_FIELD_TYPE_BLOCK_DEVICE, block_device,
+  			block_device && block_device[0] ?  ":" : NULL);
+  fs_table_line_add_field (line, field);
+
+  return line;
+}
+
+static FSTableLine *
 fs_table_line_new_from_field_values (const char *block_device,
                                      const char *mount_point,
                                      const char *fs_type,
@@ -416,6 +501,8 @@
 
 static boolean fs_table_line_is_generated (FSTableLine *line)
 {
+	if (automount)
+		return TRUE;
 
 #ifdef FSTAB_SYNC_USE_NOOP_MOUNT_OPTION
   if (!fs_table_line_has_mount_option (line, FSTAB_SYNC_MOUNT_MANAGED_KEYWORD))
@@ -468,6 +555,24 @@
     }
 }
 
+static void blat_output(size_t *output_string_length,
+		size_t *output_string_capacity,
+		char **output_string,
+		const char *val)
+{
+	  size_t field_length = strlen (val);
+
+	  if ((*output_string_length) + field_length >= (*output_string_capacity) - 1)
+		{
+		  (*output_string_capacity) *= 2;
+
+		  (*output_string) = realloc ((*output_string), (*output_string_capacity));
+		}
+
+	  strcpy ((*output_string) + (*output_string_length), val);
+	  (*output_string_length) += field_length; 
+}
+
 static char *
 fs_table_to_string (FSTable *table, size_t *length)
 {
@@ -488,19 +593,12 @@
       field = line->fields;
       while (field != NULL)
         {
-          size_t field_length;
-
-          field_length = strlen (field->value);
-
-          if (output_string_length + field_length >= output_string_capacity - 1)
-            {
-              output_string_capacity *= 2;
-
-              output_string = realloc (output_string, output_string_capacity);
-            }
-
-          strcpy (output_string + output_string_length, field->value);
-          output_string_length += field_length; 
+			if (field->prefix != NULL)
+				blat_output(&output_string_length, &output_string_capacity,
+					     &output_string, field->prefix);
+			if (field->value != NULL)
+				blat_output(&output_string_length, &output_string_capacity,
+					     &output_string, field->value);
 
           field = field->next;
         }
@@ -670,13 +768,16 @@
 {
   FSTableLine *table_line;
   FSTableField *field;
-  char *field_value, *p;
+  char *field_value, *p, *prefix;
+  int fld_idx = 0;
   FSTableFieldType current_field;
   size_t i;
 
+  fstab_update_debug (_("%d: starting fs_table_parse_line: %s\n"), pid, line);
+
   table_line = fs_table_line_new ();
 
-  current_field = FS_TABLE_FIELD_TYPE_BLOCK_DEVICE;
+  current_field = fs_fields[fld_idx];
   p = (char *) line;
   i = 0;
   while (*p != '\0' && current_field <= FS_TABLE_FIELD_TYPE_WHITE_SPACE)
@@ -724,8 +825,17 @@
           break;
         }
 
+	  if (automount && current_field == FS_TABLE_FIELD_TYPE_FILE_SYSTEM_TYPE)
+	  {
+		  while (line[i] != '\0' && !isspace (line[i]) && line[i] != ',' &&
+				  i < length)
+			i++;
+	  }
+	  else
+	  {
       while (line[i] != '\0' && !isspace (line[i]) && i < length)
         i++;
+	  }
 
       if (i > length || (line[i] == '\0' && i < length))
         {
@@ -740,10 +850,33 @@
         }
 
       assert (line + i != p);
+	  prefix = NULL;
 
+	  if (automount && current_field == FS_TABLE_FIELD_TYPE_FILE_SYSTEM_TYPE)
+	  {
+		  /* assume it starts with -fstype= */
+		  field_value = strndup (p+8, line + i - p-8);
+		  prefix = "-fstype=";
+	  }
+	  else if (automount && current_field == FS_TABLE_FIELD_TYPE_MOUNT_OPTIONS)
+	  {
+		  field_value = strndup (p+1, line + i - p-1);
+		  if (field_value[0])
+			  prefix=",";
+	  }
+	  else if (automount && current_field == FS_TABLE_FIELD_TYPE_BLOCK_DEVICE)
+	  {
+		  field_value = strndup (p+1, line + i - p-1);
+		  prefix=":";
+	  }
+	  else
       field_value = strndup (p, line + i - p);
-      field = fs_table_field_new (current_field, field_value);
-      current_field++;
+
+	  fprintf(stderr, "%d %s ", current_field, field_value);
+
+      field = autofs_table_field_new (current_field, field_value, prefix);
+      fld_idx++;
+	  current_field = fs_fields[fld_idx];
       free (field_value);
 
       fs_table_line_add_field (table_line, field);
@@ -751,6 +884,8 @@
       p = (char *) line + i;
     }
 
+  fprintf(stderr, "\n");
+
   fs_table_add_line (table, table_line);
 
   return TRUE;
@@ -1164,6 +1299,8 @@
 static boolean
 create_mount_point_for_volume (Volume *volume)
 {
+	if (automount)
+		return TRUE;
 
   /* FIXME: Should only mkdir if we need to and should do so
    * recursively for each component of the mount root.
@@ -1219,7 +1356,7 @@
 
 		    if (strcmp (volume->block_device, buf) == 0) {
 		      /* update block.device with new value */
-		      fstab_update_debug (_("%d: Found %s pointing to %s in" _PATH_FSTAB), pid, field->value, buf);
+		      fstab_update_debug (_("%d: Found %s pointing to %s in %s"), pid, field->value, buf, fstab_path);
 		      hal_device_set_property_string (hal_context, volume->udi, "block.device", field->value);
 		      return TRUE;
 		    }
@@ -1320,7 +1457,11 @@
 
   options[0] = '\0';
 
-  strcat_len (options, "noauto,user,exec");
+  strcat_len (options, "noauto,user");
+
+  if (extra_mount_opts[0])
+	  strcat_len (options, ",");
+	  strcat_len (options, extra_mount_opts);
 
 #ifdef FSTAB_SYNC_USE_NOOP_MOUNT_OPTION
   strcat_len (options, "," FSTAB_SYNC_MOUNT_MANAGED_KEYWORD);
@@ -1336,6 +1477,12 @@
     strcat_len (options, ",noatime,sync");
   }
 
+  if (automount)
+	  line = autofs_table_line_new_from_field_values (volume->block_device,
+                                              volume->mount_point,
+                                              volume->fs_type,
+                                              strdup (options), 0, 0); 
+  else
   line = fs_table_line_new_from_field_values (volume->block_device,
                                               volume->mount_point,
                                               volume->fs_type,
@@ -1436,10 +1583,20 @@
     return FALSE;
   }
 
+  if (automount)
+  {
+	  if (dev_number == 0)
+		snprintf (desired_name, sizeof (desired_name), "%s", volume->type);
+	  else
+		snprintf (desired_name, sizeof (desired_name), "%s%d", volume->type, dev_number);
+	}
+  else
+  {
   if (dev_number == 0)
     snprintf (desired_name, sizeof (desired_name), FSTAB_SYNC_MOUNT_ROOT "/%s", volume->type);
   else
     snprintf (desired_name, sizeof (desired_name), FSTAB_SYNC_MOUNT_ROOT "/%s%d", volume->type, dev_number);
+  }
 
   /* see if it's in fstab */
   for (line = table->lines; line != NULL; line = line->next) {
@@ -1449,11 +1606,14 @@
     }
   }
 
+  if (!automount)
+  {
   /* see if the mount point physically exists */
   if (stat (desired_name, &statbuf) == 0 || errno != ENOENT) {
     dev_number++;
     goto tryagain;
   }
+  }
 
   volume->mount_point = strdup (desired_name);
   return TRUE;
@@ -1482,12 +1642,12 @@
   if (volume == NULL)
     return FALSE;
 
-  dir = strdup (_PATH_FSTAB); 	 
+  dir = strdup (fstab_path); 	 
   last_slash = strrchr (dir, '/'); 	 
   if (last_slash) 
     *last_slash = '\0';
 
-  fs_table = fs_table_new (_PATH_FSTAB);
+  fs_table = fs_table_new (fstab_path);
 
   if (fs_table == NULL)
     goto error;
@@ -1497,7 +1657,7 @@
   if (fd < 0)
     goto error;
 
-  fstab_modification_time = get_file_modification_time (_PATH_FSTAB);
+  fstab_modification_time = get_file_modification_time (fstab_path);
 
   if (fstab_modification_time == 0)
     goto error;
@@ -1513,7 +1673,7 @@
 
   /* Someone changed the fs table under us, better start over.
    */
-  if (get_file_modification_time (_PATH_FSTAB) != fstab_modification_time)
+  if (get_file_modification_time (fstab_path) != fstab_modification_time)
     {
       close (fd);
       unlink (temp_filename);
@@ -1521,10 +1681,10 @@
       return add_udi (udi);
     }
 
-  if (rename (temp_filename, _PATH_FSTAB) < 0)
+  if (rename (temp_filename, fstab_path) < 0)
     {
       fstab_update_debug (_("%d: Failed to rename '%s' to '%s': %s\n"),
-                          pid, temp_filename, _PATH_FSTAB, strerror (errno));
+                          pid, temp_filename, fstab_path, strerror (errno));
       goto error;
     }
 
@@ -1539,7 +1699,7 @@
   volume_free (volume);
 
 #ifdef HAVE_SELINUX
-  restore_selinux_context(_PATH_FSTAB);
+  restore_selinux_context(fstab_path);
 #endif
 
   return TRUE;
@@ -1563,7 +1723,7 @@
   FSTableLine *line = NULL;
   char *temp_filename = NULL;
   time_t fstab_modification_time;
-  int fd;
+  int fd = -1;
   boolean is_volume;
   char *dir = NULL;
   char *last_slash;
@@ -1580,19 +1740,22 @@
 
   block_device = hal_device_get_property_string (hal_context, udi, "block.device");
 
-  dir = strdup (_PATH_FSTAB); 	 
+  if (block_device == NULL)
+	  goto error;
+
+  dir = strdup (fstab_path); 	 
   last_slash = strrchr (dir, '/'); 	 
   if (last_slash) 
     *last_slash = '\0';
 
-  fs_table = fs_table_new (_PATH_FSTAB);
+  fs_table = fs_table_new (fstab_path);
 
   fd = open_temp_fstab_file (dir, &temp_filename);
 
   if (fd < 0)
     goto error;
 
-  fstab_modification_time = get_file_modification_time (_PATH_FSTAB);
+  fstab_modification_time = get_file_modification_time (fstab_path);
 
   if (fstab_modification_time == 0)
     goto error;
@@ -1609,7 +1772,7 @@
 
   assert (line->mount_point != NULL);
 
-  if (rmdir (line->mount_point) < 0)
+  if (!automount && rmdir (line->mount_point) < 0)
     {
       fstab_update_debug (_("%d: Failed to remove mount point '%s': %s\n"),
                           pid, line->mount_point, strerror (errno));
@@ -1622,7 +1785,7 @@
 
   /* Someone changed the fs table under us, better start over.
    */
-  if (get_file_modification_time (_PATH_FSTAB) != fstab_modification_time)
+  if (get_file_modification_time (fstab_path) != fstab_modification_time)
     {
       close (fd);
       unlink (temp_filename);
@@ -1630,10 +1793,10 @@
       return remove_udi (udi);
     }
 
-  if (rename (temp_filename, _PATH_FSTAB) < 0)
+  if (rename (temp_filename, fstab_path) < 0)
     {
       fstab_update_debug (_("%d: Failed to rename '%s' to '%s': %s\n"),
-                          pid, temp_filename, _PATH_FSTAB, strerror (errno));
+                          pid, temp_filename, fstab_path, strerror (errno));
       goto error;
     }
 
@@ -1646,7 +1809,7 @@
   fs_table_line_free (line);
 
 #ifdef HAVE_SELINUX
-  restore_selinux_context(_PATH_FSTAB);
+  restore_selinux_context(fstab_path);
 #endif
 
   return TRUE;
@@ -1655,6 +1818,7 @@
   if (fd >= 0)
     close (fd);
 
+  if (block_device != NULL)
   free (block_device);
 
   if (temp_filename != NULL)
@@ -1746,19 +1910,19 @@
   char *dir = NULL;
   char *last_slash;
 
-  dir = strdup (_PATH_FSTAB); 	 
+  dir = strdup (fstab_path); 	 
   last_slash = strrchr (dir, '/'); 	 
   if (last_slash) 
     *last_slash = '\0';
 
-  fs_table = fs_table_new (_PATH_FSTAB);
+  fs_table = fs_table_new (fstab_path);
 
   fd = open_temp_fstab_file (dir, &temp_filename);
 
   if (fd < 0)
     goto error;
 
-  fstab_modification_time = get_file_modification_time (_PATH_FSTAB);
+  fstab_modification_time = get_file_modification_time (fstab_path);
 
   if (fstab_modification_time == 0)
     goto error;
@@ -1770,24 +1934,24 @@
 
   /* Someone changed the fs table under us, better start over.
    */
-  if (get_file_modification_time (_PATH_FSTAB) != fstab_modification_time)
+  if (get_file_modification_time (fstab_path) != fstab_modification_time)
     {
       close (fd);
       unlink (temp_filename);
       return clean ();
     }
 
-  if (rename (temp_filename, _PATH_FSTAB) < 0)
+  if (rename (temp_filename, fstab_path) < 0)
     {
       fstab_update_debug (_("%d: Failed to rename '%s' to '%s': %s\n"),
-                          pid, temp_filename, _PATH_FSTAB, strerror (errno));
+                          pid, temp_filename, fstab_path, strerror (errno));
       goto error;
     }
 
   close (fd);
 
 #ifdef HAVE_SELINUX
-  restore_selinux_context(_PATH_FSTAB);
+  restore_selinux_context(fstab_path);
 #endif
 
   syslog (LOG_INFO, _("removed all generated mount points"));
@@ -1804,6 +1968,51 @@
   return FALSE;
 }
 
+static int read_line(int input_fd, char *line, size_t max_read)
+{
+	/* inefficient.  i don't care.  really.  ItWorks(tm).  ILovePython(tm). */
+	size_t bytes_read;
+	size_t into = 0;
+	while ((bytes_read = read (input_fd, line, 1)) != 0 &&
+			*line != '\n' && into < max_read)
+	{
+		into++;
+		line++;
+	}
+
+	*line = 0;
+
+	return bytes_read == 1;
+
+}
+/* hacked-up function to read some additional mount options
+ * to be tacked onto all entries put into /etc/fstab (or /etc/auto.hal).
+ * e.g. you might want to specify "sync,dirsync" or "uid=fred".  or noexec.
+ * uid=fred is essential for autofs because otherwise the file system
+ * can get mounted as root...
+ */
+static void read_fstab_conf_options(void)
+{
+	int f = open("/etc/fstab-sync.conf", O_RDONLY);
+	if (f == -1)
+		return;
+	char line[80];
+	while (read_line(f, line, sizeof(line)))
+	{
+		char *p;
+		p = strchr(line, '=');
+		if (p == NULL)
+			continue;
+		*p = 0;
+		if (strcmp(line, "EXTRA_MOUNT_OPTS") == 0)
+		{
+				strncpy(extra_mount_opts, p+1, sizeof(extra_mount_opts));
+		}
+	}
+
+	close(f);
+}
+
 int
 main (int argc, const char *argv[])
 {
@@ -1814,6 +2023,20 @@
   const char **left_over_args = NULL;
   int lockfd = -1;
 
+  extra_mount_opts[0] = 0;
+  read_fstab_conf_options();
+
+  	/* choose between autofs and fstab based on last part of name of program
+	 * so we can do /etc/hal/devices.d/50-autofs.hal and expect it to work */
+	fstab_path = fstab_path;
+	fs_fields = fstab_fields;
+	if (strcmp(&(argv[0][strlen(argv[0])-10]), "autofs.hal") == 0)
+    {
+		automount = TRUE;
+		fstab_path = AUTOFS_SYNC_MOUNT_ROOT;
+		fs_fields = autofs_fields;
+    }
+
   pid = getpid ();
 
   openlog (PROGRAM_NAME, LOG_PID, LOG_USER);
@@ -1895,6 +2118,7 @@
       fstab_update_debug (_("%d: ###################################\n"), pid);
       fstab_update_debug (_("%d: %s entering; %s udi=%s\n"), 
 			  pid, PROGRAM_NAME, argv[1], hal_device_udi);
+	  if (!automount)
       fstab_update_debug (("%d: mount_root=" FSTAB_SYNC_MOUNT_ROOT 
 #ifdef FSTAB_SYNC_USE_NOOP_MOUNT_OPTION
 			   " use_managed=yes"
@@ -1902,16 +2126,15 @@
 			   " use_managed=no"
 #endif
 			   " managed_keyword=" FSTAB_SYNC_MOUNT_MANAGED_KEYWORD "\n"), pid);
-      
-      lockfd = open (_PATH_FSTAB, O_RDONLY);
+      lockfd = open (fstab_path, O_RDONLY);
       if (lockfd < 0) {
 	fstab_update_debug (_("%d: couldn't open %s O_RDONLY; bailing out\n"), 
-			    pid, _PATH_FSTAB);
+			    pid, fstab_path);
 	retval = 1;
 	goto out;
       }
-      fstab_update_debug (_("%d: Acquiring advisory lock on " 
-			    _PATH_FSTAB "\n"), pid);
+      fstab_update_debug (_("%d: Acquiring advisory lock on %s\n"),
+			  pid, fstab_path);
       if (flock (lockfd, LOCK_EX) != 0) {
 	fstab_update_debug (_("%d: Error acquiring lock '%s'; bailing out\n"),
 			    pid, strerror(errno));
@@ -1964,7 +2187,7 @@
   if (hal_device_udi != NULL) {
 
     fstab_update_debug (_("%d: Releasing advisory lock on %s\n"), 
-			pid, _PATH_FSTAB);
+			pid, fstab_path);
     if (flock (lockfd, LOCK_EX) != 0) {
       fstab_update_debug (_("%d: Error releasing lock '%s'\n"), pid, 
 	      strerror(errno));
-------------- next part --------------
_______________________________________________
hal mailing list
hal at freedesktop.org
http://freedesktop.org/mailman/listinfo/hal


More information about the Hal mailing list