Hi Willy,

> > I don't receive all the mails from haproxy@formilux.org.
> > For exemple I didn't received :
> > http://article.gmane.org/gmane.comp.web.haproxy/27795
> 
> Well, this one was sent to you directly by Cyril, so you should have
> received it.

Indeed, it was this one:
 http://article.gmane.org/gmane.comp.web.haproxy/27802

I will check my anti-spam.

Anyway :
> I've subscribed you by hand now.

I now receive all the mailling mail.
Thanks a lot.

> > > If tmp_str fails and wlt succeeds, wlt is not freed.
> > If tmp_str fails and wlt succeeds we still got the Alert and
> > everything it freed on exit.
> 
> Yes I know but as I said, if/when such code later moves to its own
> function, this function might initially decide to exit then to let
> the caller take the decision and one day all of this will be used
> dynamically or from the CLI and then people discover a memory leak.
> And there are the valgrind users who send patches very often to fix
> such warnings that annoy them. I mean we spent a lot of time killing
> some such old issues that were not bugs initially and that became
> bugs later, so we try to be careful. We don't want to be the next
> openssl if you see what I mean :-)

Yes I see :-)

For the record I now always check my code with "valgrind  --leak-
check=full".

> > I create the function "void cfgfiles_expand_directories(void)", but
> > not the "load_config_file" one.
> > I am not accustomed to using goto and it's hard for me to use it
> > here as I actually don't see the point of it (in
> > cfgfiles_expand_directories).
> 
> That's the best way to deal with error unrolling. I'm sad that
> teachers at school teach students not to use it because :
>   1) it's what the compiler implements anyway for all other
> constructs
>   2) it's the only safe way to perform unrolling which resists to
> code additions.
> 
> We used to have some leaks in the past because we were not using it.
> When you have some session initialization code like this :
> 
>     s = malloc(sizeof(*s));
>     if (!s)
>         return;
> 
>     s->req = malloc(sizeof(*s->req));
>     if (!s->req)) {
>        free(s);
>        return;
>     }
> 
>     s->res = malloc(sizeof(*s->res));
>     if (!s->res)) {
>        free(s->req);
>        free(s);
>        return;
>     }
> 
>     s->txn = malloc(sizeof(*s->txn));
>     if (!s->txn)) {
>        free(s->res);
>        free(s->req);
>        free(s);
>        return;
>     }
> 
>     s->log = malloc(sizeof(*s->log));
>     if (!s->log)) {
>        free(s->txn);
>        free(s->res);
>        free(s->req);
>        free(s);
>        return;
>     }
> 
>     s->req_capture = malloc(sizeof(*s->req_capture));
>     if (!s->req_capture)) {
>        free(s->log);
>        free(s->txn);
>        free(s->res);
>        free(s->req);
>        free(s);
>        return;
>     }
> 
>     s->res_capture = malloc(sizeof(*s->res_capture));
>     if (!s->res_capture)) {
>        free(s->req_capture);
>        free(s->log);
>        free(s->txn);
>        free(s->res);
>        free(s->req);
>        free(s);
>        return;
>     }
> 
>     ... and so on for 10 entries ...
> 
> Then you may already have bugs above due to the inevitable copy-
> paste, and once you insert a new field in the middle (eg: s->vars)
> you're pretty sure that you will miss it in one of the next "if"
> blocks because they are never as clear as above but themselves
> enclosed within other "if" blocks. And when you need to switch
> allocation order, that's even worse. But the horrible thing above can
> be safely turned into this :
> 
>     s = malloc(sizeof(*s));
>     if (!s)
>         goto fail_s;
> 
>     s->req = malloc(sizeof(*s->req));
>     if (!s->req))
>         goto fail_req;
> 
>     s->res = malloc(sizeof(*s->res));
>     if (!s->res))
>         goto fail_res;
> 
>     s->txn = malloc(sizeof(*s->txn));
>     if (!s->txn))
>         goto fail_txn;
> 
>     s->log = malloc(sizeof(*s->log));
>     if (!s->log))
>         goto fail_log;
> 
>     s->req_capture = malloc(sizeof(*s->req_capture));
>     if (!s->req_capture))
>        goto fail_req_cap;
> 
>     s->res_capture = malloc(sizeof(*s->res_capture));
>     if (!s->res_capture))
>        goto fail_res_cap;
> 
>     return s;
> 
>  fail_res_cap:
>     free(s->req_capture);
>  fail_req_cap:
>     free(s->log);
>  fail_log:
>     free(s->txn);
>  fail_txn:
>     free(s->res);
>  fail_res:
>     free(s->req);
>  fail_req:
>     free(s);
>  fail_s:
>     return NULL;
> 
> And a nice side effect is that when you look at the assembly code,
> it's much smaller and much more efficient.
> 
> > It doesn't reduce the number of lines and, as after every alert we
> > call exit, there is no need to clean anything.
> 
> As I explained above I agree on this but code correctness and code
> cleanness are two different things. We try to have a bit of
> modularity because we know that code moves and that it's better if it
> can move safely. For example I recently reimplemented all the stats
> dump to be able to dump *all* the information we have. It was
> something like 50 patches. I felt like this code had not changed for
> 10 years and that it would not change for the 10 next years. One week
> later, Thierry sent me a huge patch exploding all my functions
> and moving them around in order to call them from Lua, something I
> hadn't even imagined.

Wow, that's an explanation, thanks.

I tried to use it on the following patch.
Did I used it correctly ?

> I guess you'll find me boring or annoying initially, but
> over time once you see other people change your code and avoid a few
> traps, you'll find that such comments make sense :-)

You are not boring at all :-)
And so far you explained (with lot's of details) all the changed you
asked me to make, so that's absolutely fine by me.

> > @@ -1550,6 +1644,20 @@ void deinit(void)
> >                     free(log);
> >             }
> >     list_for_each_entry_safe(wl, wlb, &cfg_cfgfiles, list) {
> > +           int argc_i = argc - 1;
> > +           char **argv_i = argv + 1;
> > +           int is_arg = 0;
> > +
> > +           while (argc_i > 0) {
> > +                   if (*argv_i == wl->s)
> > +                           is_arg = 1;
> > +                   argv_i++;
> > +                   argc_i--;
> > +           }
> > +
> > +           if (!is_arg)
> > +                   free(wl->s);
> > +
> >             LIST_DEL(&wl->list);
> >             free(wl);
> >     }
> 
> I could have missed something but I think that the only purpose of
> this part is to decide whether or not you're going to free wl->s, am
> I right ?
> If so, why not simply free(wl->s) unconditionally ? I think I may be
> missing something.

Some "wl->s" were pointing to "argv*" elements so we can't free theme.

In the following patch I decided to create a fonction
(list_append_word) that copy it's string argument en append it to a
list.
Because it's a copy (in the heap) we now have to free every "wl->s" ;
which also make things simpler.

Regards
Maxime de Roucy

############

diff --git a/include/common/standard.h b/include/common/standard.h
index cd2208c..f123f1a 100644
--- a/include/common/standard.h
+++ b/include/common/standard.h
@@ -1089,4 +1089,12 @@ static inline unsigned long long rdtsc()
 }
 #endif
 
+/* append a copy of string <str> (in a wordlist) at the end of the list <li>
+ * On failure : return 0 and <err> filled with an error message.
+ * The caller is responsible for freeing the <err> and <str> copy
+ * memory area using free()
+ */
+struct list;
+int list_append_word(struct list *li, const char *str, char **err);
+
 #endif /* _COMMON_STANDARD_H */
diff --git a/src/haproxy.c b/src/haproxy.c
index 0c223e5..6e5eecc 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -33,10 +33,12 @@
 #include <ctype.h>
 #include <sys/time.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/socket.h>
 #include <netinet/tcp.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <dirent.h>
 #include <netdb.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -423,7 +425,7 @@ void usage(char *name)
 {
        display_version();
        fprintf(stderr,
-               "Usage : %s [-f <cfgfile>]* [ -vdV"
+               "Usage : %s [-f <cfgfile|cfgdir>]* [ -vdV"
                "D ] [ -n <maxconn> ] [ -N <maxpconn> ]\n"
                "        [ -p <pidfile> ] [ -m <max megs> ] [ -C <dir> ] [-- 
<cfgfile>*]\n"
                "        -v displays version ; -vv shows known build options.\n"
@@ -551,6 +553,87 @@ void dump(struct sig_handler *sh)
        pool_gc2();
 }
 
+/* This function check if cfg_cfgfiles containes directories.
+ * If it find one, it add all the files (and only files) it containes
+ * in cfg_cfgfiles in place of the directory (and remove the directory).
+ * It add the files in lexical order.
+ */
+void cfgfiles_expand_directories(void)
+{
+       struct wordlist *wl, *wlb;
+       char *err = NULL;
+
+       list_for_each_entry_safe(wl, wlb, &cfg_cfgfiles, list) {
+               struct stat file_stat;
+               struct dirent **dir_entries;
+               int dir_entries_nb;
+               int dir_entries_it;
+
+               if (stat(wl->s, &file_stat)) {
+                       Alert("Cannot open configuration file/directory %s : 
%s\n",
+                             wl->s,
+                             strerror(errno));
+                       exit(1);
+               }
+
+               if (!S_ISDIR(file_stat.st_mode))
+                       continue;
+
+               /* from this point wl->s is a directory */
+
+               dir_entries_nb = scandir(wl->s, &dir_entries, NULL, alphasort);
+               if (dir_entries_nb < 0) {
+                       Alert("Cannot open configuration directory %s : %s\n",
+                             wl->s,
+                             strerror(errno));
+                       exit(1);
+               }
+
+               /* for each element in the directory wl->s */
+               for (dir_entries_it = 0; dir_entries_it < dir_entries_nb; 
dir_entries_it++) {
+                       struct dirent *dir_entry = dir_entries[dir_entries_it];
+                       char *filename = NULL;
+
+                       if (!memprintf(&filename, "%s/%s", wl->s, 
dir_entry->d_name)) {
+                               Alert("Cannot load configuration files %s : out 
of memory.\n",
+                                     filename);
+                               exit(1);
+                       }
+
+                       if (stat(filename, &file_stat)) {
+                               Alert("Cannot open configuration file %s : 
%s\n",
+                                     wl->s,
+                                     strerror(errno));
+                               exit(1);
+                       }
+
+                       /* don't add anything else than regular file in 
cfg_cfgfiles
+                        * this way we avoid loops
+                        */
+                       if (S_ISREG(file_stat.st_mode)) {
+                               if (!list_append_word(&cfg_cfgfiles, filename, 
&err)) {
+                                       Alert("Cannot load configuration files 
%s : %s\n",
+                                             filename,
+                                             err);
+                                       exit(1);
+                               }
+                       }
+
+                       free(filename);
+                       free(dir_entry);
+               }
+
+               free(dir_entries);
+
+               /* remove the current directory (wl) from cfg_cfgfiles */
+               free(wl->s);
+               LIST_DEL(&wl->list);
+               free(wl);
+       }
+
+       free(err);
+}
+
 /*
  * This function initializes all the necessary variables. It only returns
  * if everything is OK. If something fails, it exits.
@@ -561,6 +644,7 @@ void init(int argc, char **argv)
        char *tmp;
        char *cfg_pidfile = NULL;
        int err_code = 0;
+       char *err_msg = NULL;
        struct wordlist *wl;
        char *progname;
        char *change_dir = NULL;
@@ -713,13 +797,12 @@ void init(int argc, char **argv)
                                /* now that's a cfgfile list */
                                argv++; argc--;
                                while (argc > 0) {
-                                       wl = calloc(1, sizeof(*wl));
-                                       if (!wl) {
-                                               Alert("Cannot load 
configuration file %s : out of memory.\n", *argv);
+                                       if (!list_append_word(&cfg_cfgfiles, 
*argv, &err_msg)) {
+                                               Alert("Cannot load 
configuration file/directory %s : %s\n",
+                                                     *argv,
+                                                     err_msg);
                                                exit(1);
                                        }
-                                       wl->s = *argv;
-                                       LIST_ADDQ(&cfg_cfgfiles, &wl->list);
                                        argv++; argc--;
                                }
                                break;
@@ -736,13 +819,12 @@ void init(int argc, char **argv)
                                case 'N' : cfg_maxpconn = atol(*argv); break;
                                case 'L' : strncpy(localpeer, *argv, 
sizeof(localpeer) - 1); break;
                                case 'f' :
-                                       wl = calloc(1, sizeof(*wl));
-                                       if (!wl) {
-                                               Alert("Cannot load 
configuration file %s : out of memory.\n", *argv);
+                                       if (!list_append_word(&cfg_cfgfiles, 
*argv, &err_msg)) {
+                                               Alert("Cannot load 
configuration file/directory %s : %s\n",
+                                                     *argv,
+                                                     err_msg);
                                                exit(1);
                                        }
-                                       wl->s = *argv;
-                                       LIST_ADDQ(&cfg_cfgfiles, &wl->list);
                                        break;
                                case 'p' : cfg_pidfile = *argv; break;
                                default: usage(progname);
@@ -758,14 +840,17 @@ void init(int argc, char **argv)
                (arg_mode & (MODE_DAEMON | MODE_SYSTEMD | MODE_FOREGROUND | 
MODE_VERBOSE
                             | MODE_QUIET | MODE_CHECK | MODE_DEBUG));
 
-       if (LIST_ISEMPTY(&cfg_cfgfiles))
-               usage(progname);
-
        if (change_dir && chdir(change_dir) < 0) {
                Alert("Could not change to directory %s : %s\n", change_dir, 
strerror(errno));
                exit(1);
        }
 
+       /* handle cfgfiles that are actualy directories */
+       cfgfiles_expand_directories();
+
+       if (LIST_ISEMPTY(&cfg_cfgfiles))
+               usage(progname);
+
        global.maxsock = 10; /* reserve 10 fds ; will be incremented by socket 
eaters */
 
        init_default_instance();
@@ -1160,6 +1245,8 @@ void init(int argc, char **argv)
        /* initialize structures for name resolution */
        if (!dns_init_resolvers())
                exit(1);
+
+       free(err_msg);
 }
 
 static void deinit_acl_cond(struct acl_cond *cond)
@@ -1550,6 +1637,7 @@ void deinit(void)
                        free(log);
                }
        list_for_each_entry_safe(wl, wlb, &cfg_cfgfiles, list) {
+               free(wl->s);
                LIST_DEL(&wl->list);
                free(wl);
        }
diff --git a/src/standard.c b/src/standard.c
index a4d2097..0de29ea 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -3439,6 +3439,38 @@ unsigned char utf8_next(const char *s, int len, unsigned 
int *c)
        return code | ((p-(unsigned char *)s)&0x0f);
 }
 
+/* append a copy of string <str> (in a wordlist) at the end of the list <li>
+ * On failure : return 0 and <err> filled with an error message.
+ * The caller is responsible for freeing the <err> and <str> copy
+ * memory area using free()
+ */
+int list_append_word(struct list *li, const char *str, char **err)
+{       
+       struct wordlist *wl;
+
+       wl = calloc(1, sizeof(*wl));
+       if (!wl) {
+               memprintf(err, "out of memory");
+               goto fail_wl;
+       }
+
+       wl->s = strdup(str);
+       if (!wl->s) {
+               memprintf(err, "out of memory");
+               goto fail_wl_s;
+       }
+
+       LIST_ADDQ(li, &wl->list);
+
+       return 1;
+
+fail_wl_s:
+       free(wl->s);
+fail_wl:
+       free(wl);
+       return 0;
+}
+
 /*
  * Local variables:
  *  c-indent-level: 8
-- 
2.8.2

Attachment: signature.asc
Description: This is a digitally signed message part

Reply via email to