Просмотр исходного кода

ncd: implement interface dependencies

ambrop7 15 лет назад
Родитель
Сommit
22db22af5d
1 измененных файлов с 215 добавлено и 58 удалено
  1. 215 58
      ncd/ncd.c

+ 215 - 58
ncd/ncd.c

@@ -55,6 +55,7 @@
 
 #define INTERFACE_TYPE_PHYSICAL 1
 
+#define INTERFACE_STATE_WAITDEPS 1
 #define INTERFACE_STATE_RESETTING 2
 #define INTERFACE_STATE_WAITDEVICE 3
 #define INTERFACE_STATE_WAITLINK 4
@@ -81,6 +82,8 @@ struct interface {
     int type;
     int state;
     BTimer reset_timer;
+    LinkedList2 deps_out;
+    LinkedList2 deps_in;
     int up;
     int have_dhcp;
     BDHCPClient dhcp;
@@ -89,6 +92,13 @@ struct interface {
     LinkedList2 ipv4_dns_servers;
 };
 
+struct dependency {
+    struct interface *src;
+    struct interface *dst;
+    LinkedList2Node src_node;
+    LinkedList2Node dst_node;
+};
+
 struct ipv4_addr_entry {
     LinkedList2Node list_node; // node in interface.ipv4_addresses
     struct ipv4_ifaddr ifaddr;
@@ -140,14 +150,19 @@ static struct interface * find_interface (const char *name);
 static void monitor_handler (void *unused, const char *ifname, int if_flags);
 static int interface_init (struct NCDConfig_interfaces *conf);
 static void interface_free (struct interface *iface);
+static void interface_down_to (struct interface *iface, int state);
 static void interface_reset (struct interface *iface);
 static void interface_start (struct interface *iface);
 static void interface_link_up (struct interface *iface);
-static void interface_link_down (struct interface *iface);
-static void interface_deconfigure (struct interface *iface);
 static void interface_log (struct interface *iface, int level, const char *fmt, ...);
 static void interface_reset_timer_handler (struct interface *iface);
 static void interface_dhcp_handler (struct interface *iface, int event);
+static void interface_remove_dependencies (struct interface *iface);
+static int interface_add_dependency (struct interface *iface, struct interface *dst);
+static void remove_dependency (struct dependency *d);
+static int interface_dependencies_satisfied (struct interface *iface);
+static void interface_satisfy_incoming_depenencies (struct interface *iface);
+static void interface_unsatisfy_incoming_depenencies (struct interface *iface);
 static int interface_set_up (struct interface *iface);
 static void interface_set_down (struct interface *iface);
 static int interface_configure_ipv4 (struct interface *iface);
@@ -586,8 +601,9 @@ void load_interfaces (struct NCDConfig_interfaces *conf)
 
 void free_interfaces (void)
 {
+    // remove in reverse order so we don't have to remove incoming dependencies
     LinkedList2Node *node;
-    while (node = LinkedList2_GetFirst(&interfaces)) {
+    while (node = LinkedList2_GetLast(&interfaces)) {
         struct interface *iface = UPPER_OBJECT(node, struct interface, list_node);
         interface_free(iface);
     }
@@ -683,12 +699,8 @@ void monitor_handler (void *unused, const char *ifname, int if_flags)
     if (!(if_flags&NCDIFCONFIG_FLAG_EXISTS)) {
         if (iface->state > INTERFACE_STATE_WAITDEVICE) {
             interface_log(iface, BLOG_INFO, "device down");
-        
-            // deconfigure
-            interface_deconfigure(iface);
             
-            // set state waitdevice
-            iface->state = INTERFACE_STATE_WAITDEVICE;
+            interface_down_to(iface, INTERFACE_STATE_WAITDEVICE);
         }
     } else {
         if (iface->state == INTERFACE_STATE_RESETTING || iface->state == INTERFACE_STATE_WAITDEVICE) {
@@ -713,7 +725,7 @@ void monitor_handler (void *unused, const char *ifname, int if_flags)
             if (iface->state > INTERFACE_STATE_WAITLINK) {
                 interface_log(iface, BLOG_INFO, "link down");
                 
-                interface_link_down(iface);
+                interface_down_to(iface, INTERFACE_STATE_WAITLINK);
             }
         }
     }
@@ -759,6 +771,35 @@ int interface_init (struct NCDConfig_interfaces *conf)
     // init reset timer
     BTimer_Init(&iface->reset_timer, INTERFACE_RETRY_TIME, (BTimer_handler)interface_reset_timer_handler, iface);
     
+    // init outgoing dependencies list
+    LinkedList2_Init(&iface->deps_out);
+    
+    // init outgoing dependencies
+    struct NCDConfig_statements *need_st = conf->statements;
+    while (need_st = find_statement(need_st, "need")) {
+        char *need_ifname;
+        if (!statement_has_one_arg(need_st, &need_ifname)) {
+            interface_log(iface, BLOG_ERROR, "need: wrong arity");
+            goto fail2;
+        }
+        
+        struct interface *need_if = find_interface(need_ifname);
+        if (!need_if) {
+            interface_log(iface, BLOG_ERROR, "need: %s: unknown interface", need_ifname);
+            goto fail2;
+        }
+        
+        if (!interface_add_dependency(iface, need_if)) {
+            interface_log(iface, BLOG_ERROR, "need: %s: failed to add dependency", need_ifname);
+            goto fail2;
+        }
+        
+        need_st = need_st->next;
+    }
+    
+    // init incoming dependencies list
+    LinkedList2_Init(&iface->deps_in);
+    
     // set not up
     iface->up = 0;
     
@@ -781,6 +822,8 @@ int interface_init (struct NCDConfig_interfaces *conf)
     
     return 1;
     
+fail2:
+    interface_remove_dependencies(iface);
 fail1:
     free(iface);
 fail0:
@@ -789,12 +832,27 @@ fail0:
 
 void interface_free (struct interface *iface)
 {
-    // deconfigure
-    interface_deconfigure(iface);
+    ASSERT(LinkedList2_IsEmpty(&iface->deps_in))
+    
+    // deconfigure IPv4
+    interface_deconfigure_ipv4(iface);
+    
+    // free DHCP
+    if (iface->have_dhcp) {
+        BDHCPClient_Free(&iface->dhcp);
+    }
+    
+    // set down
+    if (iface->up) {
+        interface_set_down(iface);
+    }
     
     // remove from interfaces list
     LinkedList2_Remove(&interfaces, &iface->list_node);
     
+    // remove outgoing dependencies
+    interface_remove_dependencies(iface);
+    
     // stop reset timer
     BReactor_RemoveTimer(&ss, &iface->reset_timer);
     
@@ -802,29 +860,64 @@ void interface_free (struct interface *iface)
     free(iface);
 }
 
-void interface_reset (struct interface *iface)
+void interface_down_to (struct interface *iface, int state)
 {
-    interface_log(iface, BLOG_INFO, "will try again later");
+    ASSERT(state >= INTERFACE_STATE_WAITDEPS)
     
-    // deconfigure
-    interface_deconfigure(iface);
+    if (state < INTERFACE_STATE_FINISHED) {
+        // unsatisfy incoming dependencies
+        interface_unsatisfy_incoming_depenencies(iface);
+        
+        // deconfigure IPv4
+        interface_deconfigure_ipv4(iface);
+    }
     
-    // set reset timer
-    BReactor_SetTimer(&ss, &iface->reset_timer);
+    // deconfigure DHCP
+    if (state < INTERFACE_STATE_DHCP && iface->have_dhcp) {
+        BDHCPClient_Free(&iface->dhcp);
+        iface->have_dhcp = 0;
+    }
+    
+    // set down
+    if (state < INTERFACE_STATE_WAITLINK && iface->up) {
+        interface_set_down(iface);
+    }
+    
+    // start/stop reset timer
+    if (state == INTERFACE_STATE_RESETTING) {
+        BReactor_SetTimer(&ss, &iface->reset_timer);
+    } else {
+        BReactor_RemoveTimer(&ss, &iface->reset_timer);
+    }
     
     // set state
-    iface->state = INTERFACE_STATE_RESETTING;
+    iface->state = state;
+}
+
+void interface_reset (struct interface *iface)
+{
+    interface_log(iface, BLOG_INFO, "will try again later");
+    
+    interface_down_to(iface, INTERFACE_STATE_RESETTING);
 }
 
 void interface_start (struct interface *iface)
 {
+    ASSERT(!BTimer_IsRunning(&iface->reset_timer))
     ASSERT(!iface->up)
     ASSERT(!iface->have_dhcp)
     ASSERT(LinkedList2_IsEmpty(&iface->ipv4_addresses))
     ASSERT(LinkedList2_IsEmpty(&iface->ipv4_routes))
     ASSERT(LinkedList2_IsEmpty(&iface->ipv4_dns_servers))
     
-    interface_log(iface, BLOG_INFO, "starting");
+    // check dependencies
+    if (!interface_dependencies_satisfied(iface)) {
+        interface_log(iface, BLOG_INFO, "waiting for dependencies");
+        
+        // waiting for dependencies
+        iface->state = INTERFACE_STATE_WAITDEPS;
+        return;
+    }
     
     // query interface state
     int flags = NCDIfConfig_query(iface->conf->name);
@@ -897,46 +990,15 @@ void interface_link_up (struct interface *iface)
     // set state finished
     iface->state = INTERFACE_STATE_FINISHED;
     
+    // satisfy incoming dependencies
+    interface_satisfy_incoming_depenencies(iface);
+    
     return;
     
 fail:
     interface_reset(iface);
 }
 
-void interface_link_down (struct interface *iface)
-{
-    ASSERT(iface->up)
-    
-    // deconfigure IPv4
-    interface_deconfigure_ipv4(iface);
-    
-    // free DHCP
-    if (iface->have_dhcp) {
-        BDHCPClient_Free(&iface->dhcp);
-        iface->have_dhcp = 0;
-    }
-    
-    // set state waitlink
-    iface->state = INTERFACE_STATE_WAITLINK;
-}
-
-void interface_deconfigure (struct interface *iface)
-{
-    // deconfigure IPv4
-    interface_deconfigure_ipv4(iface);
-    
-    // free DHCP
-    if (iface->have_dhcp) {
-        BDHCPClient_Free(&iface->dhcp);
-        iface->have_dhcp = 0;
-    }
-    
-    // set down
-    if (iface->up) {
-        interface_set_down(iface);
-    }
-}
-
 void interface_log (struct interface *iface, int level, const char *fmt, ...)
 {
     va_list vl;
@@ -972,6 +1034,9 @@ void interface_dhcp_handler (struct interface *iface, int event)
             // set state
             iface->state = INTERFACE_STATE_FINISHED;
             
+            // satisfy incoming dependencies
+            interface_satisfy_incoming_depenencies(iface);
+            
             return;
             
         fail:
@@ -983,11 +1048,7 @@ void interface_dhcp_handler (struct interface *iface, int event)
             
             interface_log(iface, BLOG_INFO, "DHCP down");
             
-            // deconfigure IPv4
-            interface_deconfigure_ipv4(iface);
-            
-            // set state
-            iface->state = INTERFACE_STATE_DHCP;
+            interface_down_to(iface, INTERFACE_STATE_DHCP);
         } break;
         
         default:
@@ -995,6 +1056,102 @@ void interface_dhcp_handler (struct interface *iface, int event)
     }
 }
 
+void interface_remove_dependencies (struct interface *iface)
+{
+    LinkedList2Node *node;
+    while (node = LinkedList2_GetFirst(&iface->deps_out)) {
+        struct dependency *d = UPPER_OBJECT(node, struct dependency, src_node);
+        ASSERT(d->src == iface)
+        
+        remove_dependency(d);
+    }
+}
+
+int interface_add_dependency (struct interface *iface, struct interface *dst_iface)
+{
+    // allocate entry
+    struct dependency *d = malloc(sizeof(*d));
+    if (!d) {
+        return 0;
+    }
+    
+    // set src and dst
+    d->src = iface;
+    d->dst = dst_iface;
+    
+    // insert to src list
+    LinkedList2_Append(&iface->deps_out, &d->src_node);
+    
+    // insert to dst list
+    LinkedList2_Append(&dst_iface->deps_in, &d->dst_node);
+    
+    return 1;
+}
+
+void remove_dependency (struct dependency *d)
+{
+    // remove from dst list
+    LinkedList2_Remove(&d->dst->deps_in, &d->dst_node);
+    
+    // remove from src list
+    LinkedList2_Remove(&d->src->deps_out, &d->src_node);
+    
+    // free entry
+    free(d);
+}
+
+int interface_dependencies_satisfied (struct interface *iface)
+{
+    LinkedList2Iterator it;
+    LinkedList2Iterator_InitForward(&it, &iface->deps_out);
+    LinkedList2Node *node;
+    while (node = LinkedList2Iterator_Next(&it)) {
+        struct dependency *d = UPPER_OBJECT(node, struct dependency, src_node);
+        ASSERT(d->src == iface)
+        
+        if (d->dst->state != INTERFACE_STATE_FINISHED) {
+            LinkedList2Iterator_Free(&it);
+            return 0;
+        }
+    }
+    
+    return 1;
+}
+
+void interface_satisfy_incoming_depenencies (struct interface *iface)
+{
+    LinkedList2Iterator it;
+    LinkedList2Iterator_InitForward(&it, &iface->deps_in);
+    LinkedList2Node *node;
+    while (node = LinkedList2Iterator_Next(&it)) {
+        struct dependency *d = UPPER_OBJECT(node, struct dependency, dst_node);
+        ASSERT(d->dst == iface)
+        
+        if (d->src->state == INTERFACE_STATE_WAITDEPS) {
+            interface_log(d->src, BLOG_INFO, "dependency %s satisfied", iface->conf->name);
+            
+            interface_start(d->src);
+        }
+    }
+}
+
+void interface_unsatisfy_incoming_depenencies (struct interface *iface)
+{
+    LinkedList2Iterator it;
+    LinkedList2Iterator_InitForward(&it, &iface->deps_in);
+    LinkedList2Node *node;
+    while (node = LinkedList2Iterator_Next(&it)) {
+        struct dependency *d = UPPER_OBJECT(node, struct dependency, dst_node);
+        ASSERT(d->dst == iface)
+        
+        if (d->src->state > INTERFACE_STATE_WAITDEPS) {
+            interface_log(d->src, BLOG_INFO, "dependency %s no longer satisfied", iface->conf->name);
+            
+            interface_down_to(d->src, INTERFACE_STATE_WAITDEPS);
+        }
+    }
+}
+
 int interface_set_up (struct interface *iface)
 {
     ASSERT(!iface->up)