unicorn 5.5.0 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.manifest +8 -5
  3. data/.olddoc.yml +15 -7
  4. data/CONTRIBUTORS +6 -2
  5. data/Documentation/.gitignore +1 -3
  6. data/Documentation/unicorn.1 +222 -0
  7. data/Documentation/unicorn_rails.1 +207 -0
  8. data/FAQ +1 -1
  9. data/GIT-VERSION-FILE +1 -1
  10. data/GIT-VERSION-GEN +1 -1
  11. data/GNUmakefile +112 -57
  12. data/HACKING +2 -9
  13. data/ISSUES +24 -31
  14. data/KNOWN_ISSUES +2 -2
  15. data/LATEST +16 -22
  16. data/Links +5 -5
  17. data/NEWS +155 -0
  18. data/README +15 -9
  19. data/SIGNALS +1 -1
  20. data/Sandbox +3 -3
  21. data/archive/slrnpull.conf +1 -1
  22. data/bin/unicorn_rails +2 -2
  23. data/examples/big_app_gc.rb +1 -1
  24. data/examples/logrotate.conf +2 -2
  25. data/examples/nginx.conf +1 -1
  26. data/examples/unicorn.conf.minimal.rb +2 -2
  27. data/examples/unicorn.conf.rb +2 -2
  28. data/examples/unicorn@.service +7 -0
  29. data/ext/unicorn_http/c_util.h +5 -13
  30. data/ext/unicorn_http/common_field_optimization.h +0 -1
  31. data/ext/unicorn_http/epollexclusive.h +124 -0
  32. data/ext/unicorn_http/ext_help.h +0 -24
  33. data/ext/unicorn_http/extconf.rb +2 -6
  34. data/ext/unicorn_http/global_variables.h +1 -1
  35. data/ext/unicorn_http/httpdate.c +1 -0
  36. data/ext/unicorn_http/unicorn_http.c +258 -228
  37. data/ext/unicorn_http/unicorn_http.rl +48 -18
  38. data/lib/unicorn/configurator.rb +13 -3
  39. data/lib/unicorn/http_request.rb +11 -1
  40. data/lib/unicorn/http_server.rb +56 -29
  41. data/lib/unicorn/oob_gc.rb +5 -5
  42. data/lib/unicorn/select_waiter.rb +6 -0
  43. data/lib/unicorn/tmpio.rb +8 -2
  44. data/lib/unicorn/version.rb +1 -1
  45. data/lib/unicorn.rb +4 -3
  46. data/man/man1/unicorn.1 +89 -88
  47. data/man/man1/unicorn_rails.1 +78 -83
  48. data/t/GNUmakefile +3 -72
  49. data/t/README +1 -1
  50. data/t/test-lib.sh +2 -1
  51. data/test/benchmark/README +14 -4
  52. data/test/benchmark/ddstream.ru +50 -0
  53. data/test/benchmark/readinput.ru +40 -0
  54. data/test/benchmark/uconnect.perl +66 -0
  55. data/test/exec/test_exec.rb +14 -12
  56. data/test/test_helper.rb +38 -30
  57. data/test/unit/test_ccc.rb +4 -3
  58. data/test/unit/test_http_parser_ng.rb +81 -0
  59. data/test/unit/test_server.rb +81 -7
  60. data/test/unit/test_signals.rb +6 -6
  61. data/test/unit/test_socket_helper.rb +1 -1
  62. data/test/unit/test_upload.rb +9 -14
  63. data/test/unit/test_util.rb +5 -4
  64. data/test/unit/test_waiter.rb +34 -0
  65. data/unicorn.gemspec +8 -7
  66. metadata +16 -11
  67. data/Documentation/GNUmakefile +0 -30
  68. data/Documentation/unicorn.1.txt +0 -187
  69. data/Documentation/unicorn_rails.1.txt +0 -175
  70. data/t/hijack.ru +0 -55
  71. data/t/t0200-rack-hijack.sh +0 -51
data/README CHANGED
@@ -12,8 +12,7 @@ both the the request and response in between unicorn and slow clients.
12
12
  cut out everything that is better supported by the operating system,
13
13
  {nginx}[https://nginx.org/] or {Rack}[https://rack.github.io/].
14
14
 
15
- * Compatible with Ruby 1.9.3 and later.
16
- unicorn 4.x remains supported for Ruby 1.8 users.
15
+ * Compatible with Ruby 2.0.0 and later.
17
16
 
18
17
  * Process management: unicorn will reap and restart workers that
19
18
  die from broken apps. There is no need to manage multiple processes
@@ -80,12 +79,12 @@ You may install it via RubyGems on RubyGems.org:
80
79
  You can get the latest source via git from the following locations
81
80
  (these versions may not be stable):
82
81
 
83
- https://bogomips.org/unicorn.git
82
+ https://yhbt.net/unicorn.git
84
83
  https://repo.or.cz/unicorn.git (mirror)
85
84
 
86
85
  You may browse the code from the web:
87
86
 
88
- * https://bogomips.org/unicorn.git
87
+ * https://yhbt.net/unicorn.git
89
88
  * https://repo.or.cz/w/unicorn.git (gitweb)
90
89
 
91
90
  See the HACKING guide on how to contribute and build prerelease gems
@@ -133,13 +132,20 @@ and libraries which run on top of it.
133
132
 
134
133
  All feedback (bug reports, user/development dicussion, patches, pull
135
134
  requests) go to the mailing list/newsgroup. See the ISSUES document for
136
- information on the {mailing list}[mailto:unicorn-public@bogomips.org].
135
+ information on the {mailing list}[mailto:unicorn-public@yhbt.net].
136
+
137
+ The mailing list is archived at https://yhbt.net/unicorn-public/
137
138
 
138
- The mailing list is archived at https://bogomips.org/unicorn-public/
139
139
  Read-only NNTP access is available at:
140
- nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn and
141
- nntp://news.gmane.org/gmane.comp.lang.ruby.unicorn.general
140
+ nntps://news.public-inbox.org/inbox.comp.lang.ruby.unicorn and
141
+ nntp://news.gmane.io/gmane.comp.lang.ruby.unicorn.general
142
+
143
+ Read-only IMAP access is also avaialble at:
144
+ imaps://yhbt.net/inbox.comp.lang.ruby.unicorn.0 and
145
+ imap://7fh6tueqddpjyxjmgtdiueylzoqt6pt7hec3pukyptlmohoowvhde4yd.onion/inbox.comp.lang.ruby.unicorn.0
146
+ The AUTH=ANONYMOUS mechanism is supported, as is any username+password
147
+ combination.
142
148
 
143
149
  For the latest on unicorn releases, you may also finger us at
144
- unicorn@bogomips.org or check our NEWS page (and subscribe to our Atom
150
+ unicorn@yhbt.net or check our NEWS page (and subscribe to our Atom
145
151
  feed).
data/SIGNALS CHANGED
@@ -8,7 +8,7 @@ should be possible to easily share process management scripts between
8
8
  Unicorn and nginx.
9
9
 
10
10
  One example init script is distributed with unicorn:
11
- https://bogomips.org/unicorn/examples/init.sh
11
+ https://yhbt.net/unicorn/examples/init.sh
12
12
 
13
13
  === Master Process
14
14
 
data/Sandbox CHANGED
@@ -34,7 +34,7 @@ is the primary issue with sandboxing tools such as Bundler and Isolate.
34
34
  If you're bundling unicorn, use "bundle exec unicorn" (or "bundle exec
35
35
  unicorn_rails") to start unicorn with the correct environment variables
36
36
 
37
- ref: https://bogomips.org/unicorn-public/9ECF07C4-5216-47BE-961D-AFC0F0C82060@internetfamo.us/
37
+ ref: https://yhbt.net/unicorn-public/9ECF07C4-5216-47BE-961D-AFC0F0C82060@internetfamo.us/
38
38
 
39
39
  Otherwise (if you choose to not sandbox your unicorn installation), we
40
40
  expect the tips for Isolate (below) apply, too.
@@ -44,7 +44,7 @@ expect the tips for Isolate (below) apply, too.
44
44
  This is no longer be an issue as of bundler 0.9.17
45
45
 
46
46
  ref:
47
- https://bogomips.org/unicorn-public/8FC34B23-5994-41CC-B5AF-7198EF06909E@tramchase.com/
47
+ https://yhbt.net/unicorn-public/8FC34B23-5994-41CC-B5AF-7198EF06909E@tramchase.com/
48
48
 
49
49
  === BUNDLE_GEMFILE for Capistrano users
50
50
 
@@ -87,7 +87,7 @@ For now workarounds include doing one of the following:
87
87
 
88
88
  3. Explicitly setting RUBYLIB or $LOAD_PATH to include any gem path
89
89
  where the unicorn gem is installed
90
- (e.g. /usr/lib/ruby/gems/1.9.3/gems/unicorn-VERSION/lib)
90
+ (e.g. /usr/lib/ruby/gems/3.0.0/gems/unicorn-VERSION/lib)
91
91
 
92
92
  === RUBYOPT pollution from SIGUSR2 upgrades
93
93
 
@@ -1,4 +1,4 @@
1
1
  # group_name max expire headers_only
2
2
  gmane.comp.lang.ruby.unicorn.general 1000000000 1000000000 0
3
3
 
4
- # usage: slrnpull -d $PWD -h news.gmane.org --no-post
4
+ # usage: slrnpull -d $PWD -h news.gmane.io --no-post
data/bin/unicorn_rails CHANGED
@@ -132,11 +132,11 @@ def rails_builder(ru, op, daemonize)
132
132
 
133
133
  # this lambda won't run until after forking if preload_app is false
134
134
  # this runs after config file reloading
135
- lambda do ||
135
+ lambda do |x, server|
136
136
  # Rails 3 includes a config.ru, use it if we find it after
137
137
  # working_directory is bound.
138
138
  ::File.exist?('config.ru') and
139
- return Unicorn.builder('config.ru', op).call
139
+ return Unicorn.builder('config.ru', op).call(x, server)
140
140
 
141
141
  # Load Rails and (possibly) the private version of Rack it bundles.
142
142
  begin
@@ -1,2 +1,2 @@
1
- # see {Unicorn::OobGC}[https://bogomips.org/unicorn/Unicorn/OobGC.html]
1
+ # see {Unicorn::OobGC}[https://yhbt.net/unicorn/Unicorn/OobGC.html]
2
2
  # Unicorn::OobGC was broken in Unicorn v3.3.1 - v3.6.1 and fixed in v3.6.2
@@ -5,7 +5,7 @@
5
5
  # https://linux.die.net/man/8/logrotate
6
6
  #
7
7
  # public logrotate-related discussion in our archives:
8
- # https://bogomips.org/unicorn-public/?q=logrotate
8
+ # https://yhbt.net/unicorn-public/?q=logrotate
9
9
 
10
10
  # Modify the following glob to match the logfiles your app writes to:
11
11
  /var/log/unicorn_app/*.log {
@@ -33,7 +33,7 @@
33
33
  systemctl kill -s SIGUSR1 unicorn@2.service
34
34
 
35
35
  # Examples for other process management systems appreciated
36
- # Mail us at unicorn-public@bogomips.org
36
+ # Mail us at unicorn-public@yhbt.net
37
37
  # (see above for archives)
38
38
 
39
39
  # If you use a pid file and assuming your pid file
data/examples/nginx.conf CHANGED
@@ -113,7 +113,7 @@ http {
113
113
  # try_files directive appeared in in nginx 0.7.27 and has stabilized
114
114
  # over time. Older versions of nginx (e.g. 0.6.x) requires
115
115
  # "if (!-f $request_filename)" which was less efficient:
116
- # https://bogomips.org/unicorn.git/tree/examples/nginx.conf?id=v3.3.1#n127
116
+ # https://yhbt.net/unicorn.git/tree/examples/nginx.conf?id=v3.3.1#n127
117
117
  try_files $uri/index.html $uri.html $uri @app;
118
118
 
119
119
  location @app {
@@ -1,9 +1,9 @@
1
1
  # Minimal sample configuration file for Unicorn (not Rack) when used
2
2
  # with daemonization (unicorn -D) started in your working directory.
3
3
  #
4
- # See https://bogomips.org/unicorn/Unicorn/Configurator.html for complete
4
+ # See https://yhbt.net/unicorn/Unicorn/Configurator.html for complete
5
5
  # documentation.
6
- # See also https://bogomips.org/unicorn/examples/unicorn.conf.rb for
6
+ # See also https://yhbt.net/unicorn/examples/unicorn.conf.rb for
7
7
  # a more verbose configuration using more features.
8
8
 
9
9
  listen 2007 # by default Unicorn listens on port 8080
@@ -2,10 +2,10 @@
2
2
  #
3
3
  # This configuration file documents many features of Unicorn
4
4
  # that may not be needed for some applications. See
5
- # https://bogomips.org/unicorn/examples/unicorn.conf.minimal.rb
5
+ # https://yhbt.net/unicorn/examples/unicorn.conf.minimal.rb
6
6
  # for a much simpler configuration file.
7
7
  #
8
- # See https://bogomips.org/unicorn/Unicorn/Configurator.html for complete
8
+ # See https://yhbt.net/unicorn/Unicorn/Configurator.html for complete
9
9
  # documentation.
10
10
 
11
11
  # Use at least one worker per core if you're on a dedicated server,
@@ -14,7 +14,14 @@ After = unicorn.socket
14
14
  # bundler users must use the "--keep-file-descriptors" switch, here:
15
15
  # ExecStart = bundle exec --keep-file-descriptors unicorn -c ...
16
16
  ExecStart = /usr/bin/unicorn -c /path/to/unicorn.conf.rb /path/to/config.ru
17
+
18
+ # NonBlocking MUST be true if using socket activation with unicorn.
19
+ # Otherwise, there's a small window in-between when the non-blocking
20
+ # flag is set by us and our accept4 call where systemd can momentarily
21
+ # make the socket blocking, causing us to block on accept4:
22
+ NonBlocking = true
17
23
  Sockets = unicorn.socket
24
+
18
25
  KillSignal = SIGQUIT
19
26
  User = nobody
20
27
  Group = nogroup
@@ -8,23 +8,15 @@
8
8
 
9
9
  #include <unistd.h>
10
10
  #include <assert.h>
11
+ #include <limits.h>
11
12
 
12
13
  #define MIN(a,b) (a < b ? a : b)
13
14
  #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
14
15
 
15
- #ifndef SIZEOF_OFF_T
16
- # define SIZEOF_OFF_T 4
17
- # warning SIZEOF_OFF_T not defined, guessing 4. Did you run extconf.rb?
18
- #endif
19
-
20
- #if SIZEOF_OFF_T == 4
21
- # define UH_OFF_T_MAX 0x7fffffff
22
- #elif SIZEOF_OFF_T == 8
23
- # if SIZEOF_LONG == 4
24
- # define UH_OFF_T_MAX 0x7fffffffffffffffLL
25
- # else
26
- # define UH_OFF_T_MAX 0x7fffffffffffffff
27
- # endif
16
+ #if SIZEOF_OFF_T == SIZEOF_INT
17
+ # define UH_OFF_T_MAX INT_MAX
18
+ #elif SIZEOF_OFF_T == SIZEOF_LONG_LONG
19
+ # define UH_OFF_T_MAX LLONG_MAX
28
20
  #else
29
21
  # error off_t size unknown for this platform!
30
22
  #endif /* SIZEOF_OFF_T check */
@@ -83,7 +83,6 @@ static void init_common_fields(void)
83
83
  struct common_field *cf = common_http_fields;
84
84
  char tmp[64];
85
85
 
86
- id_uminus = rb_intern("-@");
87
86
  memcpy(tmp, HTTP_PREFIX, HTTP_PREFIX_LEN);
88
87
 
89
88
  for(i = ARRAY_SIZE(common_http_fields); --i >= 0; cf++) {
@@ -0,0 +1,124 @@
1
+ /*
2
+ * This is only intended for use inside a unicorn worker, nowhere else.
3
+ * EPOLLEXCLUSIVE somewhat mitigates the thundering herd problem for
4
+ * mostly idle processes since we can't use blocking accept4.
5
+ * This is NOT intended for use with multi-threaded servers, nor
6
+ * single-threaded multi-client ("C10K") servers or anything advanced
7
+ * like that. This use of epoll is only appropriate for a primitive,
8
+ * single-client, single-threaded servers like unicorn that need to
9
+ * support SIGKILL timeouts and parent death detection.
10
+ */
11
+ #if defined(HAVE_EPOLL_CREATE1)
12
+ # include <sys/epoll.h>
13
+ # include <errno.h>
14
+ # include <ruby/io.h>
15
+ # include <ruby/thread.h>
16
+ #endif /* __linux__ */
17
+
18
+ #if defined(EPOLLEXCLUSIVE) && defined(HAVE_EPOLL_CREATE1)
19
+ # define USE_EPOLL (1)
20
+ #else
21
+ # define USE_EPOLL (0)
22
+ #endif
23
+
24
+ #if USE_EPOLL
25
+ /*
26
+ * :nodoc:
27
+ * returns IO object if EPOLLEXCLUSIVE works and arms readers
28
+ */
29
+ static VALUE prep_readers(VALUE cls, VALUE readers)
30
+ {
31
+ long i;
32
+ int epfd = epoll_create1(EPOLL_CLOEXEC);
33
+ VALUE epio;
34
+
35
+ if (epfd < 0) rb_sys_fail("epoll_create1");
36
+
37
+ epio = rb_funcall(cls, rb_intern("for_fd"), 1, INT2NUM(epfd));
38
+
39
+ Check_Type(readers, T_ARRAY);
40
+ for (i = 0; i < RARRAY_LEN(readers); i++) {
41
+ int rc;
42
+ struct epoll_event e;
43
+ rb_io_t *fptr;
44
+ VALUE io = rb_ary_entry(readers, i);
45
+
46
+ e.data.u64 = i; /* the reason readers shouldn't change */
47
+
48
+ /*
49
+ * I wanted to use EPOLLET here, but maintaining our own
50
+ * equivalent of ep->rdllist in Ruby-space doesn't fit
51
+ * our design at all (and the kernel already has it's own
52
+ * code path for doing it). So let the kernel spend
53
+ * cycles on maintaining level-triggering.
54
+ */
55
+ e.events = EPOLLEXCLUSIVE | EPOLLIN;
56
+ io = rb_io_get_io(io);
57
+ GetOpenFile(io, fptr);
58
+ rc = epoll_ctl(epfd, EPOLL_CTL_ADD, fptr->fd, &e);
59
+ if (rc < 0) rb_sys_fail("epoll_ctl");
60
+ }
61
+ return epio;
62
+ }
63
+ #endif /* USE_EPOLL */
64
+
65
+ #if USE_EPOLL
66
+ struct ep_wait {
67
+ struct epoll_event *events;
68
+ rb_io_t *fptr;
69
+ int maxevents;
70
+ int timeout_msec;
71
+ };
72
+
73
+ static void *do_wait(void *ptr) /* runs w/o GVL */
74
+ {
75
+ struct ep_wait *epw = ptr;
76
+
77
+ return (void *)(long)epoll_wait(epw->fptr->fd, epw->events,
78
+ epw->maxevents, epw->timeout_msec);
79
+ }
80
+
81
+ /* :nodoc: */
82
+ /* readers must not change between prepare_readers and get_readers */
83
+ static VALUE
84
+ get_readers(VALUE epio, VALUE ready, VALUE readers, VALUE timeout_msec)
85
+ {
86
+ struct ep_wait epw;
87
+ long i, n;
88
+ VALUE buf;
89
+
90
+ Check_Type(ready, T_ARRAY);
91
+ Check_Type(readers, T_ARRAY);
92
+ epw.maxevents = RARRAY_LENINT(readers);
93
+ buf = rb_str_buf_new(sizeof(struct epoll_event) * epw.maxevents);
94
+ epw.events = (struct epoll_event *)RSTRING_PTR(buf);
95
+ epio = rb_io_get_io(epio);
96
+ GetOpenFile(epio, epw.fptr);
97
+
98
+ epw.timeout_msec = NUM2INT(timeout_msec);
99
+ n = (long)rb_thread_call_without_gvl(do_wait, &epw, RUBY_UBF_IO, NULL);
100
+ if (n < 0) {
101
+ if (errno != EINTR) rb_sys_fail("epoll_wait");
102
+ n = 0;
103
+ }
104
+ /* Linux delivers events in order received */
105
+ for (i = 0; i < n; i++) {
106
+ struct epoll_event *ev = &epw.events[i];
107
+ VALUE obj = rb_ary_entry(readers, ev->data.u64);
108
+
109
+ if (RTEST(obj))
110
+ rb_ary_push(ready, obj);
111
+ }
112
+ rb_str_resize(buf, 0);
113
+ return Qfalse;
114
+ }
115
+ #endif /* USE_EPOLL */
116
+
117
+ static void init_epollexclusive(VALUE mUnicorn)
118
+ {
119
+ #if USE_EPOLL
120
+ VALUE cWaiter = rb_define_class_under(mUnicorn, "Waiter", rb_cIO);
121
+ rb_define_singleton_method(cWaiter, "prep_readers", prep_readers, 1);
122
+ rb_define_method(cWaiter, "get_readers", get_readers, 3);
123
+ #endif
124
+ }
@@ -8,30 +8,6 @@
8
8
  # define assert_frozen(f) do {} while (0)
9
9
  #endif /* !defined(OBJ_FROZEN) */
10
10
 
11
- #if !defined(OFFT2NUM)
12
- # if SIZEOF_OFF_T == SIZEOF_LONG
13
- # define OFFT2NUM(n) LONG2NUM(n)
14
- # else
15
- # define OFFT2NUM(n) LL2NUM(n)
16
- # endif
17
- #endif /* ! defined(OFFT2NUM) */
18
-
19
- #if !defined(SIZET2NUM)
20
- # if SIZEOF_SIZE_T == SIZEOF_LONG
21
- # define SIZET2NUM(n) ULONG2NUM(n)
22
- # else
23
- # define SIZET2NUM(n) ULL2NUM(n)
24
- # endif
25
- #endif /* ! defined(SIZET2NUM) */
26
-
27
- #if !defined(NUM2SIZET)
28
- # if SIZEOF_SIZE_T == SIZEOF_LONG
29
- # define NUM2SIZET(n) ((size_t)NUM2ULONG(n))
30
- # else
31
- # define NUM2SIZET(n) ((size_t)NUM2ULL(n))
32
- # endif
33
- #endif /* ! defined(NUM2SIZET) */
34
-
35
11
  static inline int str_cstr_eq(VALUE val, const char *ptr, long len)
36
12
  {
37
13
  return (RSTRING_LEN(val) == len && !memcmp(ptr, RSTRING_PTR(val), len));
@@ -1,12 +1,7 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'mkmf'
3
3
 
4
- have_macro("SIZEOF_OFF_T", "ruby.h") or check_sizeof("off_t", "sys/types.h")
5
- have_macro("SIZEOF_SIZE_T", "ruby.h") or check_sizeof("size_t", "sys/types.h")
6
- have_macro("SIZEOF_LONG", "ruby.h") or check_sizeof("long", "sys/types.h")
7
- have_func("rb_str_set_len", "ruby.h") or abort 'Ruby 1.9.3+ required'
8
- have_func("rb_hash_clear", "ruby.h") # Ruby 2.0+
9
- have_func("gmtime_r", "time.h")
4
+ have_func("rb_hash_clear", "ruby.h") or abort 'Ruby 2.0+ required'
10
5
 
11
6
  message('checking if String#-@ (str_uminus) dedupes... ')
12
7
  begin
@@ -38,4 +33,5 @@ else
38
33
  message("no, needs Ruby 2.6+\n")
39
34
  end
40
35
 
36
+ have_func('epoll_create1', %w(sys/epoll.h))
41
37
  create_makefile("unicorn_http")
@@ -55,7 +55,7 @@ NORETURN(static void parser_raise(VALUE klass, const char *));
55
55
 
56
56
  /** Defines global strings in the init method. */
57
57
  #define DEF_GLOBAL(N, val) do { \
58
- g_##N = rb_obj_freeze(rb_str_new(val, sizeof(val) - 1)); \
58
+ g_##N = str_new_dd_freeze(val, (long)sizeof(val) - 1); \
59
59
  rb_gc_register_mark_object(g_##N); \
60
60
  } while (0)
61
61
 
@@ -11,6 +11,7 @@ static const char months[] = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
11
11
 
12
12
  /* for people on wonky systems only */
13
13
  #ifndef HAVE_GMTIME_R
14
+ # warning using fake gmtime_r
14
15
  static struct tm * my_gmtime_r(time_t *now, struct tm *tm)
15
16
  {
16
17
  struct tm *global = gmtime(now);