hotplug2: merge worker related fixes into one patch
[openwrt.git] / package / hotplug2 / patches / 140-worker_fork_fix.patch
1 --- a/action.c
2 +++ b/action.c
3 @@ -39,7 +39,7 @@ static void action_dumb(const struct set
4   * Returns: Newly allocated string in "key=value" form
5   *
6   */
7 -static char* alloc_env(const char *key, const char *value) {
8 +char* alloc_env(const char *key, const char *value) {
9         size_t keylen, vallen;
10         char *combined;
11  
12 --- a/action.h
13 +++ b/action.h
14 @@ -12,5 +12,6 @@
15  #include "settings.h"
16  
17  void action_perform(struct settings_t *, struct uevent_t *);
18 +char* alloc_env(const char *, const char *);
19  #endif /* ifndef ACTION_H */
20  
21 --- a/workers/worker_fork.c
22 +++ b/workers/worker_fork.c
23 @@ -1,6 +1,69 @@
24  #include "worker_fork.h"
25  
26  static struct worker_fork_ctx_t *global_ctx;
27 +static struct worker_fork_uevent_t *uevent_list;
28 +
29 +static void worker_fork_uevent_free(struct worker_fork_uevent_t *node) {
30 +       uevent_free(node->uevent);
31 +       free(node);
32 +}
33 +
34 +static void worker_fork_uevent_add(void *in_ctx, struct uevent_t *uevent) {
35 +       char **env;
36 +       int i;
37 +       struct worker_fork_ctx_t *ctx = in_ctx;
38 +       struct worker_fork_uevent_t *node, *walker;
39 +
40 +       node = malloc(sizeof (struct worker_fork_uevent_t));
41 +       node->uevent = uevent_dup(uevent);
42 +       node->next = NULL;
43 +
44 +       if (!uevent_list) uevent_list = node;
45 +       else {
46 +               /*
47 +                * Put events that need to fork first and in reverse order
48 +                */
49 +               env = xmalloc(sizeof(char *) * node->uevent->env_vars_c);
50 +               for (i = 0; i < node->uevent->env_vars_c; i++) {
51 +                       env[i] = alloc_env(node->uevent->env_vars[i].key, node->uevent->env_vars[i].value);
52 +                       putenv(env[i]);
53 +               }
54 +               if (ruleset_flags(&ctx->settings->rules, uevent) & FLAG_SLOW) {
55 +                       node->next = uevent_list;
56 +                       uevent_list = node;
57 +               }
58 +               else {
59 +                       for (walker = uevent_list; walker->next; walker = walker->next);
60 +                       walker->next = node;
61 +               }
62 +               for (i = 0; i < node->uevent->env_vars_c; i++) {
63 +                       unsetenv(node->uevent->env_vars[i].key);
64 +                       free(env[i]);
65 +               }
66 +               free(env);
67 +       }
68 +}
69 +
70 +static void worker_fork_uevent_del(struct worker_fork_uevent_t *node) {
71 +       struct worker_fork_uevent_t *walker;
72 +
73 +       if (node == uevent_list) {
74 +               uevent_list = node->next;
75 +       }
76 +       else {
77 +               for (walker = uevent_list; walker->next; walker = walker->next)
78 +                       if (walker->next == node) walker->next = node->next;
79 +       }
80 +       worker_fork_uevent_free(node);
81 +}
82 +
83 +static void worker_fork_uevent_empty(void) {
84 +       struct worker_fork_uevent_t *walker;
85 +
86 +       if (!uevent_list) return;
87 +       for (walker = uevent_list; walker->next; walker = walker->next) worker_fork_uevent_free(walker);
88 +       uevent_list = NULL;
89 +}
90  
91  /**
92   * Destroys data structures related to the given child ID (not PID).
93 @@ -315,6 +378,8 @@ static void *worker_fork_init(struct set
94         struct worker_fork_ctx_t *ctx;
95         PRINTFUNC();
96  
97 +       uevent_list = NULL;
98 +
99         ctx = malloc(sizeof(struct worker_fork_ctx_t));
100         ctx->children = NULL;
101         ctx->children_count = 0;
102 @@ -376,26 +441,39 @@ static void worker_fork_deinit(void *in_
103         free(ctx->children);
104         free(ctx);
105         global_ctx = NULL;
106 +       worker_fork_uevent_empty();
107  }
108  
109  
110  static int worker_fork_process(void *in_ctx, struct uevent_t *uevent) {
111 +       char **env;
112         int i;
113         struct worker_fork_child_t *child;
114         struct worker_fork_ctx_t *ctx = in_ctx;
115 +       struct worker_fork_uevent_t *node, *walker;
116 +       event_seqnum_t seqnum;
117 +
118 +       worker_fork_uevent_add(ctx, uevent);
119 +       walker = uevent_list;
120  
121         /*
122 -        * A big loop, because if we fail to process the event,
123 +        * A big loop, because if we fail to process the events,
124          * we don't want to give up.
125          *
126          * TODO: Decide if we want to limit the number of attempts
127          * or set a time limit before reporting terminal failure.
128          */
129         do {
130 +               /*
131 +                * If more events are waiting, return to receive them
132 +                */
133 +               if (!seqnum_get(&seqnum) && seqnum > uevent->seqnum) break;
134 +
135 +               node = walker;
136                 worker_fork_update_children(ctx);
137  
138                 child = NULL;
139 -               for (i = 0; i < ctx->children_count; i++) {
140 +               for (i = 0; i < ctx->children_count && i < ctx->max_children; i++) {
141                         if (ctx->children[i]->busy == 0) {
142                                 child = ctx->children[i];
143                                 break;
144 @@ -406,21 +484,37 @@ static int worker_fork_process(void *in_
145                  * No child process is currently available.
146                  */
147                 if (child == NULL) {
148 +                       env = xmalloc(sizeof(char *) * node->uevent->env_vars_c);
149 +                       for (i = 0; i < node->uevent->env_vars_c; i++) {
150 +                               env[i] = alloc_env(node->uevent->env_vars[i].key, node->uevent->env_vars[i].value);
151 +                               putenv(env[i]);
152 +                       }
153 +
154                         /*
155                          * Are the matching rules trivial enough that we
156                          * can execute them in the main process?
157                          */
158                         if (ctx->always_fork == 0 && ctx->settings->dumb == 0 && 
159 -                       (ruleset_flags(&ctx->settings->rules, uevent) & FLAG_MASK_SLOW) == 0) {
160 -                               action_perform(ctx->settings, uevent);
161 +                       (ruleset_flags(&ctx->settings->rules, node->uevent) & FLAG_MASK_SLOW) == 0) {
162 +                               action_perform(ctx->settings, node->uevent);
163 +                               walker = walker->next;
164 +                               worker_fork_uevent_del(node);
165 +                               if (walker) continue;
166                                 break;
167                         }
168 -                       
169 +
170                         /*
171                          * We have to fork off a new child.
172                          */
173 -                       if (ctx->children_count < ctx->max_children)
174 +                       if (ctx->children_count < ctx->max_children ||
175 +                       (ruleset_flags(&ctx->settings->rules, node->uevent) & FLAG_SLOW))
176                                 child = worker_fork_spawn(ctx);
177 +
178 +                       for (i = 0; i < node->uevent->env_vars_c; i++) {
179 +                               unsetenv(node->uevent->env_vars[i].key);
180 +                               free(env[i]);
181 +                       }
182 +                       free(env);
183                 }
184  
185                 /*
186 @@ -428,9 +522,14 @@ static int worker_fork_process(void *in_
187                  */
188                 if (child != NULL) {
189                         child->busy = 1;
190 -                       if (!worker_fork_relay_event(child->event_fd, uevent));
191 -                               break;
192 -                       child->busy = 0;
193 +                       if (worker_fork_relay_event(child->event_fd, node->uevent)) {
194 +                               child->busy = 0;
195 +                               continue;
196 +                       }
197 +                       walker = walker->next;
198 +                       worker_fork_uevent_del(node);
199 +                       if (walker) continue;
200 +                       break;
201                 }
202  
203                 /* 
204 --- a/uevent.c
205 +++ b/uevent.c
206 @@ -132,6 +132,8 @@ struct uevent_t *uevent_dup(const struct
207         
208         dest = xmalloc(sizeof(struct uevent_t));
209         dest->action = src->action;
210 +       dest->seqnum = src->seqnum;
211 +       dest->action_str = strdup(src->action_str);
212         dest->env_vars_c = src->env_vars_c;
213         dest->env_vars = xmalloc(sizeof(struct env_var_t) * dest->env_vars_c);
214         dest->plain_s = src->plain_s;
215 --- a/workers/worker_fork.h
216 +++ b/workers/worker_fork.h
217 @@ -35,4 +35,9 @@ struct worker_fork_ctx_t {
218         struct settings_t                       *settings;
219  };
220  
221 +struct worker_fork_uevent_t {
222 +       struct uevent_t *uevent;
223 +       struct worker_fork_uevent_t *next;
224 +};
225 +
226  #endif