2006-06-25 20:48:02 -04:00
/*
* HA - Proxy : High Availability - enabled HTTP / TCP proxy
[RELEASE] Released version 1.9-dev1
Released version 1.9-dev1 with the following main changes :
- BUG/MEDIUM: kqueue: Don't bother closing the kqueue after fork.
- DOC: cache: update sections and fix some typos
- BUILD/MINOR: deviceatlas: enable thread support
- BUG/MEDIUM: tcp-check: Don't lock the server in tcpcheck_main
- BUG/MEDIUM: ssl: don't allocate shctx several time
- BUG/MEDIUM: cache: bad computation of the remaining size
- BUILD: checks: don't include server.h
- BUG/MEDIUM: stream: fix session leak on applet-initiated connections
- BUILD/MINOR: haproxy : FreeBSD/cpu affinity needs pthread_np header
- BUILD/MINOR: Makefile : enabling USE_CPU_AFFINITY
- BUG/MINOR: ssl: CO_FL_EARLY_DATA removal is managed by stream
- BUG/MEDIUM: threads/peers: decrement, not increment jobs on quitting
- BUG/MEDIUM: h2: don't report an error after parsing a 100-continue response
- BUG/MEDIUM: peers: fix some track counter rules dont register entries for sync.
- BUG/MAJOR: thread/peers: fix deadlock on peers sync.
- BUILD/MINOR: haproxy: compiling config cpu parsing handling when needed
- MINOR: config: report when "monitor fail" rules are misplaced
- BUG/MINOR: mworker: fix validity check for the pipe FDs
- BUG/MINOR: mworker: detach from tty when in daemon mode
- MINOR: threads: Fix pthread_setaffinity_np on FreeBSD.
- BUG/MAJOR: thread: Be sure to request a sync between threads only once at a time
- BUILD: Fix LDFLAGS vs. LIBS re linking order in various makefiles
- BUG/MEDIUM: checks: Be sure we have a mux if we created a cs.
- BUG/MINOR: hpack: fix debugging output of pseudo header names
- BUG/MINOR: hpack: must reject huffman literals padded with more than 7 bits
- BUG/MINOR: hpack: reject invalid header index
- BUG/MINOR: hpack: dynamic table size updates are only allowed before headers
- BUG/MAJOR: h2: correctly check the request length when building an H1 request
- BUG/MINOR: h2: immediately close if receiving GOAWAY after the last stream
- BUG/MINOR: h2: try to abort closed streams as soon as possible
- BUG/MINOR: h2: ":path" must not be empty
- BUG/MINOR: h2: fix a typo causing PING/ACK to be responded to
- BUG/MINOR: h2: the TE header if present may only contain trailers
- BUG/MEDIUM: h2: enforce the per-connection stream limit
- BUG/MINOR: h2: do not accept SETTINGS_ENABLE_PUSH other than 0 or 1
- BUG/MINOR: h2: reject incorrect stream dependencies on HEADERS frame
- BUG/MINOR: h2: properly check PRIORITY frames
- BUG/MINOR: h2: reject response pseudo-headers from requests
- BUG/MEDIUM: h2: remove connection-specific headers from request
- BUG/MEDIUM: h2: do not accept upper case letters in request header names
- BUG/MINOR: h2: use the H2_F_DATA_* macros for DATA frames
- BUG/MINOR: action: Don't check http capture rules when no id is defined
- BUG/MAJOR: hpack: don't pretend large headers fit in empty table
- BUG/MINOR: ssl: support tune.ssl.cachesize 0 again
- BUG/MEDIUM: mworker: also close peers sockets in the master
- BUG/MEDIUM: ssl engines: Fix async engines fds were not considered to fix fd limit automatically.
- BUG/MEDIUM: checks: a down server going to maint remains definitely stucked on down state.
- BUG/MEDIUM: peers: set NOLINGER on the outgoing stream interface
- BUG/MEDIUM: h2: fix handling of end of stream again
- MINOR: mworker: Update messages referencing exit-on-failure
- MINOR: mworker: Improve wording in `void mworker_wait()`
- CONTRIB: halog: Add help text for -s switch in halog program
- BUG/MEDIUM: email-alert: don't set server check status from a email-alert task
- BUG/MEDIUM: threads/vars: Fix deadlock in register_name
- MINOR: systemd: remove comment about HAPROXY_STATS_SOCKET
- DOC: notifications: add precisions about thread usage
- BUG/MEDIUM: lua/notification: memory leak
- MINOR: conn_stream: add new flag CS_FL_RCV_MORE to indicate pending data
- BUG/MEDIUM: stream-int: always set SI_FL_WAIT_ROOM on CS_FL_RCV_MORE
- BUG/MEDIUM: h2: automatically set CS_FL_RCV_MORE when the output buffer is full
- BUG/MEDIUM: h2: enable recv polling whenever demuxing is possible
- BUG/MEDIUM: h2: work around a connection API limitation
- BUG/MEDIUM: h2: debug incoming traffic in h2_wake()
- MINOR: h2: store the demux padding length in the h2c struct
- BUG/MEDIUM: h2: support uploading partial DATA frames
- MINOR: h2: don't demand that a DATA frame is complete before processing it
- BUG/MEDIUM: h2: don't switch the state to HREM before end of DATA frame
- BUG/MEDIUM: h2: don't close after the first DATA frame on tunnelled responses
- BUG/MEDIUM: http: don't disable lingering on requests with tunnelled responses
- BUG/MEDIUM: h2: fix stream limit enforcement
- BUG/MINOR: stream-int: don't try to receive again after receiving an EOS
- MINOR: sample: add len converter
- BUG: MAJOR: lb_map: server map calculation broken
- BUG: MINOR: http: don't check http-request capture id when len is provided
- MINOR: sample: rename the "len" converter to "length"
- BUG/MEDIUM: mworker: Set FD_CLOEXEC flag on log fd
- DOC/MINOR: intro: typo, wording, formatting fixes
- MINOR: netscaler: respect syntax
- MINOR: netscaler: remove the use of cip_magic only used once
- MINOR: netscaler: rename cip_len to clarify its uage
- BUG/MEDIUM: netscaler: use the appropriate IPv6 header size
- BUG/MAJOR: netscaler: address truncated CIP header detection
- MINOR: netscaler: check in one-shot if buffer is large enough for IP and TCP header
- MEDIUM: netscaler: do not analyze original IP packet size
- MEDIUM: netscaler: add support for standard NetScaler CIP protocol
- MINOR: spoe: add force-set-var option in spoe-agent configuration
- CONTRIB: iprange: Fix compiler warning in iprange.c
- CONTRIB: halog: Fix compiler warnings in halog.c
- BUG/MINOR: h2: properly report a stream error on RST_STREAM
- MINOR: mux: add flags to describe a mux's capabilities
- MINOR: stream-int: set flag SI_FL_CLEAN_ABRT when mux supports clean aborts
- BUG/MEDIUM: stream: don't consider abortonclose on muxes which close cleanly
- BUG/MEDIUM: checks: a server passed in maint state was not forced down.
- BUG/MEDIUM: lua: fix crash when using bogus mode in register_service()
- MINOR: http: adjust the list of supposedly cacheable methods
- MINOR: http: update the list of cacheable status codes as per RFC7231
- MINOR: http: start to compute the transaction's cacheability from the request
- BUG/MINOR: http: do not ignore cache-control: public
- BUG/MINOR: http: properly detect max-age=0 and s-maxage=0 in responses
- BUG/MINOR: cache: do not force the TX_CACHEABLE flag before checking cacheability
- MINOR: http: add a function to check request's cache-control header field
- BUG/MEDIUM: cache: do not try to retrieve host-less requests from the cache
- BUG/MEDIUM: cache: replace old object on store
- BUG/MEDIUM: cache: respect the request cache-control header
- BUG/MEDIUM: cache: don't cache the response on no-cache="set-cookie"
- BUG/MAJOR: connection: refine the situations where we don't send shutw()
- BUG/MEDIUM: checks: properly set servers to stopping state on 404
- BUG/MEDIUM: h2: properly handle and report some stream errors
- BUG/MEDIUM: h2: improve handling of frames received on closed streams
- DOC/MINOR: configuration: typo, formatting fixes
- BUG/MEDIUM: h2: ensure we always know the stream before sending a reset
- BUG/MEDIUM: mworker: don't close stdio several time
- MINOR: don't close stdio anymore
- BUG/MEDIUM: http: don't automatically forward request close
- BUG/MAJOR: hpack: don't return direct references to the dynamic headers table
- MINOR: h2: add a function to report pseudo-header names
- DEBUG: hpack: make hpack_dht_dump() expose the output file
- DEBUG: hpack: add more traces to the hpack decoder
- CONTRIB: hpack: add an hpack decoder
- MEDIUM: h2: prepare a graceful shutdown when the frontend is stopped
- BUG/MEDIUM: h2: properly handle the END_STREAM flag on empty DATA frames
- BUILD: ssl: silence a warning when building without NPN nor ALPN support
- CLEANUP: rbtree: remove
- BUG/MEDIUM: ssl: cache doesn't release shctx blocks
- BUG/MINOR: lua: Fix default value for pattern in Socket.receive
- DOC: lua: Fix typos in comments of hlua_socket_receive
- BUG/MEDIUM: lua: Fix IPv6 with separate port support for Socket.connect
- BUG/MINOR: lua: Fix return value of Socket.settimeout
- MINOR: dns: Handle SRV record weight correctly.
- BUG/MEDIUM: mworker: execvp failure depending on argv[0]
- MINOR: hathreads: add support for gcc < 4.7
- BUILD/MINOR: ancient gcc versions atomic fix
- BUG/MEDIUM: stream: properly handle client aborts during redispatch
- MINOR: spoe: add register-var-names directive in spoe-agent configuration
- MINOR: spoe: Don't queue a SPOE context if nothing is sent
- DOC: clarify the scope of ssl_fc_is_resumed
- CONTRIB: debug: fix a few flags definitions
- BUG/MINOR: poll: too large size allocation for FD events
- MINOR: sample: add date_us sample
- BUG/MEDIUM: peers: fix expire date wasn't updated if entry is modified remotely.
- MINOR: servers: Don't report duplicate dyncookies for disabled servers.
- MINOR: global/threads: move cpu_map at the end of the global struct
- MINOR: threads: add a MAX_THREADS define instead of LONGBITS
- MINOR: global: add some global activity counters to help debugging
- MINOR: threads/fd: Use a bitfield to know if there are FDs for a thread in the FD cache
- BUG/MEDIUM: threads/polling: Use fd_cache_mask instead of fd_cache_num
- BUG/MEDIUM: fd: maintain a per-thread update mask
- MINOR: fd: add a bitmask to indicate that an FD is known by the poller
- BUG/MEDIUM: epoll/threads: use one epoll_fd per thread
- BUG/MEDIUM: kqueue/threads: use one kqueue_fd per thread
- BUG/MEDIUM: threads/mworker: fix a race on startup
- BUG/MINOR: mworker: only write to pidfile if it exists
- MINOR: threads: Fix build when we're not compiling with threads.
- BUG/MINOR: threads: always set an owner to the thread_sync pipe
- BUG/MEDIUM: threads/server: Fix deadlock in srv_set_stopping/srv_set_admin_flag
- BUG/MEDIUM: checks: Don't try to release undefined conn_stream when a check is freed
- BUG/MINOR: kqueue/threads: Don't forget to close kqueue_fd[tid] on each thread
- MINOR: threads: Use __decl_hathreads instead of #ifdef/#endif
- BUILD: epoll/threads: Add test on MAX_THREADS to avoid warnings when complied without threads
- BUILD: kqueue/threads: Add test on MAX_THREADS to avoid warnings when complied without threads
- CLEANUP: sample: Fix comment encoding of sample.c
- CLEANUP: sample: Fix outdated comment about sample casts functions
- BUG/MINOR: sample: Fix output type of c_ipv62ip
- CLEANUP: Fix typo in ARGT_MSK6 comment
- CLEANUP: standard: Use len2mask4 in str2mask
- MINOR: standard: Add str2mask6 function
- MINOR: config: Add support for ARGT_MSK6
- MEDIUM: sample: Add IPv6 support to the ipmask converter
- MINOR: config: Enable tracking of up to MAX_SESS_STKCTR stick counters.
- BUG/MINOR: cli: use global.maxsock and not maxfd to list all FDs
- MINOR: polling: make epoll and kqueue not depend on maxfd anymore
- MINOR: fd: don't report maxfd in alert messages
- MEDIUM: polling: start to move maxfd computation to the pollers
- CLEANUP: fd/threads: remove the now unused fdtab_lock
- MINOR: poll: more accurately compute the new maxfd in the loop
- CLEANUP: fd: remove the unused "new" field
- MINOR: fd: move the hap_fd_{clr,set,isset} functions to fd.h
- MEDIUM: select: make use of hap_fd_* functions
- MEDIUM: fd: use atomic ops for hap_fd_{clr,set} and remove poll_lock
- MEDIUM: select: don't use the old FD state anymore
- MEDIUM: poll: don't use the old FD state anymore
- MINOR: fd: pass the iocb and owner to fd_insert()
- BUG/MINOR: threads: Update labels array because of changes in lock_label enum
- MINOR: stick-tables: Adds support for new "gpc1" and "gpc1_rate" counters.
- BUG/MINOR: epoll/threads: only call epoll_ctl(DEL) on polled FDs
- DOC: don't suggest using http-server-close
- MINOR: introduce proxy-v2-options for send-proxy-v2
- BUG/MEDIUM: spoe: Always try to receive or send the frame to detect shutdowns
- BUG/MEDIUM: spoe: Allow producer to read and to forward shutdown on request side
- MINOR: spoe: Remove check on min_applets number when a SPOE context is queued
- MINOR: spoe: Always link a SPOE context with the applet processing it
- MINOR: spoe: Replace sending_rate by a frequency counter
- MINOR: spoe: Count the number of frames waiting for an ack for each applet
- MEDIUM: spoe: Use an ebtree to manage idle applets
- MINOR: spoa_example: Count the number of frames processed by each worker
- MINOR: spoe: Add max-waiting-frames directive in spoe-agent configuration
- MINOR: init: make stdout unbuffered
- MINOR: early data: Don't rely on CO_FL_EARLY_DATA to wake up streams.
- MINOR: early data: Never remove the CO_FL_EARLY_DATA flag.
- MINOR: compiler: introduce offsetoff().
- MINOR: threads: Introduce double-width CAS on x86_64 and arm.
- MINOR: threads: add test and set/reset operations
- MINOR: pools/threads: Implement lockless memory pools.
- MAJOR: fd/threads: Make the fdcache mostly lockless.
- MEDIUM: fd/threads: Make sure we don't miss a fd cache entry.
- MAJOR: fd: compute the new fd polling state out of the fd lock
- MINOR: epoll: get rid of the now useless fd_compute_new_polled_status()
- MINOR: kqueue: get rid of the now useless fd_compute_new_polled_status()
- MINOR: poll: get rid of the now useless fd_compute_new_polled_status()
- MINOR: select: get rid of the now useless fd_compute_new_polled_status()
- CLEANUP: fd: remove the now unused fd_compute_new_polled_status() function
- MEDIUM: fd: make updt_fd_polling() use atomics
- MEDIUM: poller: use atomic ops to update the fdtab mask
- MINOR: fd: move the fd_{add_to,rm_from}_fdlist functions to fd.c
- BUG/MINOR: fd/threads: properly dereference fdcache as volatile
- MINOR: fd: remove the unneeded last CAS when adding an fd to the list
- MINOR: fd: reorder fd_add_to_fd_list()
- BUG/MINOR: time/threads: ensure the adjusted time is always correct
- BUG/MEDIUM: standard: Fix memory leak in str2ip2()
- MINOR: init: emit warning when -sf/-sd cannot parse argument
- BUILD: fd/threads: fix breakage build breakage without threads
- DOC: Describe routing impact of using interface keyword on bind lines
- DOC: Mention -Ws in the list of available options
- BUG/MINOR: config: don't emit a warning when global stats is incompletely configured
- BUG/MINOR: fd/threads: properly lock the FD before adding it to the fd cache.
- BUG/MEDIUM: threads: fix the double CAS implementation for ARMv7
- BUG/MEDIUM: ssl: Don't always treat SSL_ERROR_SYSCALL as unrecovarable.
- BUILD/MINOR: memory: stdint is needed for uintptr_t
- BUG/MINOR: init: Add missing brackets in the code parsing -sf/-st
- DOC: lua: new prototype for function "register_action()"
- DOC: cfgparse: Warn on option (tcp|http)log in backend
- BUG/MINOR: ssl/threads: Make management of the TLS ticket keys files thread-safe
- MINOR: sample: add a new "concat" converter
- BUG/MEDIUM: ssl: Shutdown the connection for reading on SSL_ERROR_SYSCALL
- BUG/MEDIUM: http: Switch the HTTP response in tunnel mode as earlier as possible
- BUG/MEDIUM: ssl/sample: ssl_bc_* fetch keywords are broken.
- MINOR: ssl/sample: adds ssl_bc_is_resumed fetch keyword.
- CLEANUP: cfgparse: Remove unused label end
- CLEANUP: spoe: Remove unused label retry
- CLEANUP: h2: Remove unused labels from mux_h2.c
- CLEANUP: pools: Remove unused end label in memory.h
- CLEANUP: standard: Fix typo in IPv6 mask example
- BUG/MINOR: pools/threads: don't ignore DEBUG_UAF on double-word CAS capable archs
- BUG/MINOR: debug/pools: properly handle out-of-memory when building with DEBUG_UAF
- MINOR: debug/pools: make DEBUG_UAF also detect underflows
- MINOR: stats: display the number of threads in the statistics.
- BUG/MINOR: h2: Set the target of dbuf_wait to h2c
- BUG/MEDIUM: h2: always consume any trailing data after end of output buffers
- BUG/MEDIUM: buffer: Fix the wrapping case in bo_putblk
- BUG/MEDIUM: buffer: Fix the wrapping case in bi_putblk
- BUG/MEDIUM: spoe: Remove idle applets from idle list when HAProxy is stopping
- Revert "BUG/MINOR: send-proxy-v2: string size must include ('\0')"
- MINOR: ssl: extract full pkey info in load_certificate
- MINOR: ssl: add ssl_sock_get_pkey_algo function
- MINOR: ssl: add ssl_sock_get_cert_sig function
- MINOR: connection: add proxy-v2-options ssl-cipher,cert-sig,cert-key
- MINOR: connection: add proxy-v2-options authority
- MINOR: systemd: Add section for SystemD sandboxing to unit file
- MINOR: systemd: Add SystemD's Protect*= options to the unit file
- MINOR: systemd: Add SystemD's SystemCallFilter option to the unit file
- CLEANUP: h2: rename misleading h2c_stream_close() to h2s_close()
- MINOR: h2: provide and use h2s_detach() and h2s_free()
- MEDIUM: h2: use a single buffer allocator
- MINOR/BUILD: fix Lua build on Mac OS X
- BUILD/MINOR: fix Lua build on Mac OS X (again)
- BUG/MINOR: session: Fix tcp-request session failure if handshake.
- CLEANUP: .gitignore: Ignore binaries from the contrib directory
- BUG/MINOR: unix: Don't mess up when removing the socket from the xfer_sock_list.
- DOC: buffers: clarify the purpose of the <from> pointer in offer_buffers()
- BUG/MEDIUM: h2: also arm the h2 timeout when sending
- BUG/MINOR: cli: Fix a crash when passing a negative or too large value to "show fd"
- CLEANUP: ssl: Remove a duplicated #include
- CLEANUP: cli: Remove a leftover debug message
- BUG/MINOR: cli: Fix a typo in the 'set rate-limit' usage
- BUG/MEDIUM: fix a 100% cpu usage with cpu-map and nbthread/nbproc
- BUG/MINOR: force-persist and ignore-persist only apply to backends
- BUG/MEDIUM: threads/unix: Fix a deadlock when a listener is temporarily disabled
- BUG/MAJOR: threads/queue: Fix thread-safety issues on the queues management
- BUG/MINOR: dns: don't downgrade DNS accepted payload size automatically
- TESTS: Add a testcase for multi-port + multi-server listener issue
- CLEANUP: dns: remove duplicate code in src/dns.c
- BUG/MINOR: seemless reload: Fix crash when an interface is specified.
- BUG/MINOR: cli: Ensure all command outputs end with a LF
- BUG/MINOR: cli: Fix a crash when sending a command with too many arguments
- BUILD: ssl: Fix build with OpenSSL without NPN capability
- BUG/MINOR: spoa-example: unexpected behavior for more than 127 args
- BUG/MINOR: lua: return bad error messages
- CLEANUP: lua/syntax: lua is a name and not an acronym
- BUG/MEDIUM: tcp-check: single connect rule can't detect DOWN servers
- BUG/MINOR: tcp-check: use the server's service port as a fallback
- BUG/MEDIUM: threads/queue: wake up other threads upon dequeue
- MINOR: log: stop emitting alerts when it's not possible to write on the socket
- BUILD/BUG: enable -fno-strict-overflow by default
- BUG/MEDIUM: fd/threads: ensure the fdcache_mask always reflects the cache contents
- DOC: log: more than 2 log servers are allowed
- MINOR: hash: add new function hash_crc32c
- MINOR: proxy-v2-options: add crc32c
- MINOR: accept-proxy: support proxy protocol v2 CRC32c checksum
- REORG: compact "struct server"
- MINOR: samples: add crc32c converter
- BUG/MEDIUM: h2: properly account for DATA padding in flow control
- BUG/MINOR: h2: ensure we can never send an RST_STREAM in response to an RST_STREAM
- BUG/MINOR: listener: Don't decrease actconn twice when a new session is rejected
- CLEANUP: map, stream: remove duplicate code in src/map.c, src/stream.c
- BUG/MINOR: lua: the function returns anything
- BUG/MINOR: lua funtion hlua_socket_settimeout don't check negative values
- CLEANUP: lua: typo fix in comments
- BUILD/MINOR: fix build when USE_THREAD is not defined
- MINOR: lua: allow socket api settimeout to accept integers, float, and doubles
- BUG/MINOR: hpack: fix harmless use of uninitialized value in hpack_dht_insert
- MINOR: cli/threads: make "show fd" report thread_sync_io_handler instead of "unknown"
- MINOR: cli: make "show fd" report the mux and mux_ctx pointers when available
- BUILD/MINOR: cli: fix a build warning introduced by last commit
- BUG/MAJOR: h2: remove orphaned streams from the send list before closing
- MINOR: h2: always call h2s_detach() in h2_detach()
- MINOR: h2: fuse h2s_detach() and h2s_free() into h2s_destroy()
- BUG/MEDIUM: h2/threads: never release the task outside of the task handler
- BUG/MEDIUM: h2: don't consider pending data on detach if connection is in error
- BUILD/MINOR: threads: always export thread_sync_io_handler()
- MINOR: mux: add a "show_fd" function to dump debugging information for "show fd"
- MINOR: h2: implement a basic "show_fd" function
- MINOR: cli: report cache indexes in "show fd"
- BUG/MINOR: h2: remove accidental debug code introduced with show_fd function
- BUG/MEDIUM: h2: always add a stream to the send or fctl list when blocked
- BUG/MINOR: checks: check the conn_stream's readiness and not the connection
- BUG/MINOR: fd: Don't clear the update_mask in fd_insert.
- BUG/MINOR: email-alert: Set the mailer port during alert initialization
- BUG/MINOR: cache: fix "show cache" output
- BUG/MAJOR: cache: fix random crashes caused by incorrect delete() on non-first blocks
- BUG/MINOR: spoe: Initialize variables used during conf parsing before any check
- BUG/MINOR: spoe: Don't release the context buffer in .check_timeouts callbaclk
- BUG/MINOR: spoe: Register the variable to set when an error occurred
- BUG/MINOR: spoe: Don't forget to decrement fpa when a processing is interrupted
- MINOR: spoe: Add metrics in to know time spent in the SPOE
- MINOR: spoe: Add options to store processing times in variables
- MINOR: log: move 'log' keyword parsing in dedicated function
- MINOR: log: Keep the ref when a log server is copied to avoid duplicate entries
- MINOR: spoe: Add loggers dedicated to the SPOE agent
- MINOR: spoe: Add support for option dontlog-normal in the SPOE agent section
- MINOR: spoe: use agent's logger to log SPOE messages
- MINOR: spoe: Add counters to log info about SPOE agents
- BUG/MAJOR: cache: always initialize newly created objects
- MINOR: servers: Support alphanumeric characters for the server templates names
- BUG/MEDIUM: threads: Fix the max/min calculation because of name clashes
- BUG/MEDIUM: connection: Make sure we have a mux before calling detach().
- BUG/MINOR: http: Return an error in proxy mode when url2sa fails
- MINOR: proxy: Add fe_defbe fetcher
- MINOR: config: Warn if resolvers has no nameservers
- BUG/MINOR: cli: Guard against NULL messages when using CLI_ST_PRINT_FREE
- MINOR: cli: Ensure the CLI always outputs an error when it should
- MEDIUM: sample: Extend functionality for field/word converters
- MINOR: export localpeer as an environment variable
- BUG/MEDIUM: kqueue: When adding new events, provide an output to get errors.
- BUILD: sample: avoid build warning in sample.c
- BUG/CRITICAL: h2: fix incorrect frame length check
- DOC: lua: update the links to the config and Lua API
- BUG/MINOR: pattern: Add a missing HA_SPIN_INIT() in pat_ref_newid()
- BUG/MAJOR: channel: Fix crash when trying to read from a closed socket
- BUG/MINOR: log: t_idle (%Ti) is not set for some requests
- BUG/MEDIUM: lua: Fix segmentation fault if a Lua task exits
- MINOR: h2: detect presence of CONNECT and/or content-length
- BUG/MEDIUM: h2: implement missing support for chunked encoded uploads
- BUG/MINOR: spoe: Fix counters update when processing is interrupted
- BUG/MINOR: spoe: Fix parsing of dontlog-normal option
- MEDIUM: cli: Add payload support
- MINOR: map: Add payload support to "add map"
- MINOR: ssl: Add payload support to "set ssl ocsp-response"
- BUG/MINOR: lua/threads: Make lua's tasks sticky to the current thread
- MINOR: sample: Add strcmp sample converter
- MINOR: http: Add support for 421 Misdirected Request
- BUG/MINOR: config: disable http-reuse on TCP proxies
- MINOR: ssl: disable SSL sample fetches when unsupported
- MINOR: ssl: add fetch 'ssl_fc_session_key' and 'ssl_bc_session_key'
- BUG/MINOR: checks: Fix check->health computation for flapping servers
- BUG/MEDIUM: threads: Fix the sync point for more than 32 threads
- BUG/MINOR, BUG/MINOR: lua: Put tasks to sleep when waiting for data
- MINOR: backend: implement random-based load balancing
- DOC/MINOR: clean up LUA documentation re: servers & array/table.
- MINOR: lua: Add server name & puid to LUA Server class.
- MINOR: lua: add get_maxconn and set_maxconn to LUA Server class.
- BUG/MINOR: map: correctly track reference to the last ref_elt being dumped
- BUG/MEDIUM: task: Don't free a task that is about to be run.
- MINOR: fd: Make the lockless fd list work with multiple lists.
- BUG/MEDIUM: pollers: Use a global list for fd shared between threads.
- MINOR: pollers: move polled_mask outside of struct fdtab.
- BUG/MINOR: lua: schedule socket task upon lua connect()
- BUG/MINOR: lua: ensure large proxy IDs can be represented
- BUG/MEDIUM: pollers/kqueue: use incremented position in event list
- BUG/MINOR: cli: don't stop cli_gen_usage_msg() when kw->usage == NULL
- BUG/MEDIUM: http: don't always abort transfers on CF_SHUTR
- BUG/MEDIUM: ssl: properly protect SSL cert generation
- BUG/MINOR: lua: Socket.send threw runtime error: 'close' needs 1 arguments.
- BUG/MINOR: spoe: Mistake in error message about SPOE configuration
- BUG/MEDIUM: spoe: Flags are not encoded in network order
- CLEANUP: spoe: Remove unused variables the agent structure
- DOC: spoe: fix a typo
- BUG/MEDIUM: contrib/mod_defender: Use network order to encode/decode flags
- BUG/MEDIUM: contrib/modsecurity: Use network order to encode/decode flags
- DOC: add some description of the pending rework of the buffer structure
- BUG/MINOR: ssl/lua: prevent lua from affecting automatic maxconn computation
- MINOR: lua: Improve error message
- BUG/MEDIUM: cache: don't cache when an Authorization header is present
- MINOR: ssl: set SSL_OP_PRIORITIZE_CHACHA
- BUG/MEDIUM: dns: Delay the attempt to run a DNS resolution on check failure.
- BUG/BUILD: threads: unbreak build without threads
- BUG/MEDIUM: servers: Add srv_addr default placeholder to the state file
- BUG/MEDIUM: lua/socket: Length required read doesn't work
- MINOR: tasks: Change the task API so that the callback takes 3 arguments.
- MAJOR: tasks: Create a per-thread runqueue.
- MAJOR: tasks: Introduce tasklets.
- MINOR: tasks: Make the number of tasks to run at once configurable.
- MAJOR: applets: Use tasks, instead of rolling our own scheduler.
- BUG/MEDIUM: stick-tables: Decrement ref_cnt in table_* converters
- MINOR: http: Log warning if (add|set)-header fails
- DOC: management: add the new wrew stats column
- MINOR: stats: also report the failed header rewrites warnings on the stats page
- BUG/MEDIUM: tasks: Don't forget to increase/decrease tasks_run_queue.
- BUG/MEDIUM: task: Don't forget to decrement max_processed after each task.
- MINOR: task: Also consider the task list size when getting global tasks.
- MINOR: dns: Implement `parse-resolv-conf` directive
- BUG/MEDIUM: spoe: Return an error when the wrong ACK is received in sync mode
- MINOR: task/notification: Is notifications registered ?
- BUG/MEDIUM: lua/socket: wrong scheduling for sockets
- BUG/MAJOR: lua: Dead lock with sockets
- BUG/MEDIUM: lua/socket: Notification error
- BUG/MEDIUM: lua/socket: Sheduling error on write: may dead-lock
- BUG/MEDIUM: lua/socket: Buffer error, may segfault
- DOC: contrib/modsecurity: few typo fixes
- DOC: SPOE.txt: fix a typo
- MAJOR: spoe: upgrade the SPOP version to 2.0 and remove the support for 1.0
- BUG/MINOR: contrib/spoa_example: Don't reset the status code during disconnect
- BUG/MINOR: contrib/mod_defender: Don't reset the status code during disconnect
- BUG/MINOR: contrib/modsecurity: Don't reset the status code during disconnect
- BUG/MINOR: contrib/mod_defender: update pointer on the end of the frame
- BUG/MINOR: contrib/modsecurity: update pointer on the end of the frame
- MINOR: task: Fix a compiler warning by adding a cast.
- MINOR: stats: also report the nice and number of calls for applets
- MINOR: applet: assign the same nice value to a new appctx as its owner task
- MINOR: task: Fix compiler warning.
- BUG/MEDIUM: tasks: Use the local runqueue when building without threads.
- MINOR: tasks: Don't define rqueue if we're building without threads.
- BUG/MINOR: unix: Make sure we can transfer abns sockets on seamless reload.
- MINOR: lua: Increase debug information
- BUG/MEDIUM: threads: handle signal queue only in thread 0
- BUG/MINOR: don't ignore SIG{BUS,FPE,ILL,SEGV} during signal processing
- BUG/MINOR: signals: ha_sigmask macro for multithreading
- BUG/MAJOR: map: fix a segfault when using http-request set-map
- DOC: regression testing: Add a short starting guide.
- MINOR: tasks: Make sure we correctly init and deinit a tasklet.
- BUG/MINOR: tasklets: Just make sure we don't pass a tasklet to the handler.
- BUG/MINOR: lua: Segfaults with wrong usage of types.
- BUG/MAJOR: ssl: Random crash with cipherlist capture
- BUG/MAJOR: ssl: OpenSSL context is stored in non-reserved memory slot
- BUG/MEDIUM: ssl: do not store pkinfo with SSL_set_ex_data
- MINOR: tests: First regression testing file.
- MINOR: reg-tests: Add reg-tests/README file.
- MINOR: reg-tests: Add a few regression testing files.
- DOC: Add new REGTEST tag info about reg testing.
- BUG/MEDIUM: fd: Don't modify the update_mask in fd_dodelete().
- MINOR: Some spelling cleanup in the comments.
- BUG/MEDIUM: threads: Use the sync point to check active jobs and exit
- MINOR: threads: Be sure to remove threads from all_threads_mask on exit
- REGTEST/MINOR: Wrong URI in a reg test for SSL/TLS.
- REGTEST/MINOR: Set HAPROXY_PROGRAM default value.
- REGTEST/MINOR: Add levels to reg-tests target.
- BUG/MAJOR: Stick-tables crash with segfault when the key is not in the stick-table
- BUG/BUILD: threads: unbreak build without threads
- BUG/MAJOR: stick_table: Complete incomplete SEGV fix
- MINOR: stick-tables: make stktable_release() do nothing on NULL
- BUG/MEDIUM: lua: possible CLOSE-WAIT state with '\n' headers
- MINOR: startup: change session/process group settings
- MINOR: systemd: consider exit status 143 as successful
- REGTEST/MINOR: Wrong URI syntax.
- CLEANUP: dns: remove obsolete macro DNS_MAX_IP_REC
- CLEANUP: dns: inacurate comment about prefered IP score
- MINOR: dns: fix wrong score computation in dns_get_ip_from_response
- MINOR: dns: new DNS options to allow/prevent IP address duplication
- REGTEST/MINOR: Unexpected curl URL globling.
- BUG/MINOR: ssl: properly ref-count the tls_keys entries
- MINOR: h2: keep a count of the number of conn_streams attached to the mux
- BUG/MEDIUM: h2: don't accept new streams if conn_streams are still in excess
- MINOR: h2: add the mux and demux buffer lengths on "show fd"
- BUG/MEDIUM: h2: never leave pending data in the output buffer on close
- BUG/MEDIUM: h2: make sure the last stream closes the connection after a timeout
- MINOR: tasklet: Set process to NULL.
- MINOR: buffer: implement a new file for low-level buffer manipulation functions
- MINOR: buffer: switch buffer sizes and offsets to size_t
- MINOR: buffer: add a few basic functions for the new API
- MINOR: buffer: Introduce b_sub(), b_add(), and bo_add()
- MINOR: buffer: Add b_set_data().
- MINOR: buffer: introduce b_realign_if_empty()
- MINOR: compression: pass the channel to http_compression_buffer_end()
- MINOR: channel: add a few basic functions for the new buffer API
- MINOR: channel/buffer: use c_realign_if_empty() instead of buffer_realign()
- MINOR: channel/buffer: replace buffer_slow_realign() with channel_slow_realign() and b_slow_realign()
- MEDIUM: channel: make channel_slow_realign() take a swap buffer
- MINOR: h2: use b_slow_realign() with the trash as a swap buffer
- MINOR: buffer: remove buffer_slow_realign() and the swap_buffer allocation code
- MINOR: channel/buffer: replace b_{adv,rew} with c_{adv,rew}
- MINOR: buffer: replace calls to buffer_space_wraps() with b_space_wraps()
- MINOR: buffer: remove bi_getblk() and bi_getblk_nc()
- MINOR: buffer: split bi_contig_data() into ci_contig_data and b_config_data()
- MINOR: buffer: remove bi_ptr()
- MINOR: buffer: remove bo_ptr()
- MINOR: buffer: remove bo_end()
- MINOR: buffer: remove bi_end()
- MINOR: buffer: remove bo_contig_data()
- MINOR: buffer: merge b{i,o}_contig_space()
- MINOR: buffer: replace bo_getblk() with direction agnostic b_getblk()
- MINOR: buffer: replace bo_getblk_nc() with b_getblk_nc() which takes an offset
- MINOR: buffer: replace bi_del() and bo_del() with b_del()
- MINOR: buffer: convert most b_ptr() calls to c_ptr()
- MINOR: h1: make h1_measure_trailers() take the byte count in argument
- MINOR: h2: clarify the fact that the send functions are unsigned
- MEDIUM: h2: prevent the various mux encoders from modifying the buffer
- MINOR: h1: make h1_skip_chunk_crlf() not depend on b_ptr() anymore
- MINOR: h1: make h1_parse_chunk_size() not depend on b_ptr() anymore
- MINOR: h1: make h1_measure_trailers() use an offset and a count
- MEDIUM: h2: do not use buf->o anymore inside h2_snd_buf's loop
- MEDIUM: h2: don't use b_ptr() nor b_end() anymore
- MINOR: buffer: get rid of b_end() and b_to_end()
- MINOR: buffer: make b_getblk_nc() take const pointers
- MINOR: buffer: make b_getblk_nc() take size_t for the block sizes
- MEDIUM: connection: make xprt->snd_buf() take the byte count in argument
- MEDIUM: mux: make mux->snd_buf() take the byte count in argument
- MEDIUM: connection: make xprt->rcv_buf() use size_t for the count
- MEDIUM: mux: make mux->rcv_buf() take a size_t for the count
- MINOR: connection: add a flags argument to rcv_buf()
- MINOR: connection: add a new receive flag : CO_RFL_BUF_WET
- MINOR: buffer: get rid of b_ptr() and convert its last users
- MINOR: buffer: use b_room() to determine available space in a buffer
- MINOR: buffer: replace buffer_not_empty() with b_data() or c_data()
- MINOR: buffer: replace buffer_empty() with b_empty() or c_empty()
- MINOR: buffer: make bo_putchar() use b_tail()
- MINOR: buffer: replace buffer_full() with channel_full()
- MINOR: buffer: replace bi_space_for_replace() with ci_space_for_replace()
- MINOR: buffer: replace buffer_pending() with ci_data()
- MINOR: buffer: replace buffer_flush() with c_adv(chn, ci_data(chn))
- MINOR: buffer: use c_head() instead of buffer_wrap_sub(c->buf, p-o)
- MINOR: buffer: use b_orig() to replace most references to b->data
- MINOR: buffer: Use b_add()/bo_add() instead of accessing b->i/b->o.
- MINOR: channel: remove almost all references to buf->i and buf->o
- MINOR: channel: Add co_set_data().
- MEDIUM: channel: adapt to the new buffer API
- MINOR: checks: adapt to the new buffer API
- MEDIUM: h2: update to the new buffer API
- MINOR: buffer: remove unused bo_add()
- MEDIUM: spoe: use the new buffer API for the SPOE buffer
- MINOR: stats: adapt to the new buffers API
- MINOR: cli: use the new buffer API
- MINOR: cache: use the new buffer API
- MINOR: stream-int: use the new buffer API
- MINOR: stream: use wrappers instead of directly manipulating buffers
- MINOR: backend: use new buffer API
- MEDIUM: http: use wrappers instead of directly manipulating buffers states
- MINOR: filters: convert to the new buffer API
- MINOR: payload: convert to the new buffer API
- MEDIUM: h1: port to new buffer API.
- MINOR: flt_trace: adapt to the new buffer API
- MEDIUM: compression: start to move to the new buffer API
- MINOR: lua: use the wrappers instead of directly manipulating buffer states
- MINOR: buffer: convert part bo_putblk() and bi_putblk() to the new API
- MINOR: buffer: adapt buffer_slow_realign() and buffer_dump() to the new API
- MAJOR: start to change buffer API
- MINOR: buffer: remove the check for output on b_del()
- MINOR: buffer: b_set_data() doesn't truncate output data anymore
- MINOR: buffer: rename the "data" field to "area"
- MEDIUM: buffers: move "output" from struct buffer to struct channel
- MINOR: buffer: replace bi_fast_delete() with b_del()
- MINOR: buffer: replace b{i,o}_put* with b_put*
- MINOR: buffer: add a new file for ist + buffer manipulation functions
- MINOR: checks: use b_putist() instead of b_putstr()
- MINOR: buffers: remove b_putstr()
- CLEANUP: buffer: minor cleanups to buffer.h
- MINOR: buffers/channel: replace buffer_insert_line2() with ci_insert_line2()
- MINOR: buffer: replace buffer_replace2() with b_rep_blk()
- MINOR: buffer: rename the data length member to '->data'
- MAJOR: buffer: finalize buffer detachment
- MEDIUM: chunks: make the chunk struct's fields match the buffer struct
- MAJOR: chunks: replace struct chunk with struct buffer
- DOC: buffers: document the new buffers API
- DOC: buffers: remove obsolete docs about buffers
- MINOR: tasklets: Don't attempt to add a tasklet in the list twice.
- MINOR: connections/mux: Add a new "subscribe" method.
- MEDIUM: connections/mux: Revamp the send direction.
- MINOR: connection: simplify subscription by adding a registration function
- BUG/MINOR: http: Set brackets for the unlikely macro at the right place
- BUG/MINOR: build: Fix compilation with debug mode enabled
- BUILD: Generate sha256 checksums in publish-release
- MINOR: debug: Add check for CO_FL_WILL_UPDATE
- MINOR: debug: Add checks for conn_stream flags
- MINOR: ist: Add the function isteqi
- BUG/MEDIUM: threads: Fix the exit condition of the thread barrier
- BUG/MEDIUM: mux_h2: Call h2_send() before updating polling.
- MINOR: buffers: simplify b_contig_space()
- MINOR: buffers: split b_putblk() into __b_putblk()
- MINOR: buffers: add b_xfer() to transfer data between buffers
- DOC: add some design notes about the new layering model
- MINOR: conn_stream: add a new CS_FL_REOS flag
- MINOR: conn_stream: add an rx buffer to the conn_stream
- MEDIUM: conn_stream: add cs_recv() as a default rcv_buf() function
- MEDIUM: stream-int: automatically call si_cs_recv_cb() if the cs has data on wake()
- MINOR: h2: make each H2 stream support an intermediary input buffer
- MEDIUM: h2: make h2_frt_decode_headers() use an intermediary buffer
- MEDIUM: h2: make h2_frt_transfer_data() copy via an intermediary buffer
- MEDIUM: h2: centralize transfer of decoded frames in h2_rcv_buf()
- MEDIUM: h2: move headers and data frame decoding to their respective parsers
- MEDIUM: buffers: make b_xfer() automatically swap buffers when possible
- MEDIUM: h2: perform a single call to the data layer in demux()
- MEDIUM: h2: don't call data_cb->recv() anymore
- MINOR: h2: make use of CS_FL_REOS to indicate that end of stream was seen
- MEDIUM: h2: use the default conn_stream's receive function
- DOC: add more design feedback on the new layering model
- MINOR: h2: add the error code and the max/last stream IDs to "show fd"
- BUG/MEDIUM: stream-int: don't immediately enable reading when the buffer was reportedly full
- BUG/MEDIUM: stats: don't ask for more data as long as we're responding
- BUG/MINOR: servers: Don't make "server" in a frontend fatal.
- BUG/MEDIUM: tasks: make sure we pick all tasks in the run queue
- BUG/MEDIUM: tasks: Decrement rqueue_size at the right time.
- BUG/MEDIUM: tasks: use atomic ops for active_tasks_mask
- BUG/MEDIUM: tasks: Make sure there's no task left before considering inactive.
- MINOR: signal: don't pass the signal number anymore as the wakeup reason
- MINOR: tasks: extend the state bits from 8 to 16 and remove the reason
- MINOR: tasks: Add a flag that tells if we're in the global runqueue.
- BUG/MEDIUM: tasks: make __task_unlink_rq responsible for the rqueue size.
- MINOR: queue: centralize dequeuing code a bit better
- MEDIUM: queue: make pendconn_free() work on the stream instead
- DOC: queue: document the expected locking model for the server's queue
- MINOR: queue: make sure pendconn->strm->pend_pos is always valid
- MINOR: queue: use a distinct variable for the assigned server and the queue
- MINOR: queue: implement pendconn queue locking functions
- MEDIUM: queue: get rid of the pendconn lock
- MINOR: tasks: Make active_tasks_mask volatile.
- MINOR: tasks: Make global_tasks_mask volatile.
- MINOR: pollers: Add a way to wake a thread sleeping in the poller.
- MINOR: threads/queue: Get rid of THREAD_WANT_SYNC in the queue code.
- BUG/MEDIUM: threads/sync: use sched_yield when available
- MINOR: ssl: BoringSSL matches OpenSSL 1.1.0
- BUG/MEDIUM: h2: prevent orphaned streams from blocking a connection forever
- BUG/MINOR: config: stick-table is not supported in defaults section
- BUILD/MINOR: threads: unbreak build with threads disabled
- BUG/MINOR: threads: Handle nbthread == MAX_THREADS.
- BUG/MEDIUM: threads: properly fix nbthreads == MAX_THREADS
- MINOR: threads: move "nbthread" parsing to hathreads.c
- BUG/MEDIUM: threads: unbreak "bind" referencing an incorrect thread number
- MEDIUM: proxy_protocol: Convert IPs to v6 when protocols are mixed
- BUILD/MINOR: compiler: fix offsetof() on older compilers
- SCRIPTS: git-show-backports: add missing quotes to "echo"
- MINOR: threads: add more consistency between certain variables in no-thread case
- MEDIUM: hathreads: implement a more flexible rendez-vous point
- BUG/MEDIUM: cli: make "show fd" thread-safe
2018-08-02 12:12:50 -04:00
* Copyright 2000 - 2018 Willy Tarreau < willy @ haproxy . org > .
2006-06-25 20:48:02 -04:00
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*
2017-04-28 09:24:30 -04:00
* Please refer to RFC7230 - RFC7235 informations about HTTP protocol , and
* RFC6265 for informations about cookies usage . More generally , the IETF HTTP
2006-06-25 20:48:02 -04:00
* Working Group ' s web site should be consulted for protocol related changes :
*
* http : //ftp.ics.uci.edu/pub/ietf/http/
*
* Pending bugs ( may be not fixed because never reproduced ) :
* - solaris only : sometimes , an HTTP proxy with only a dispatch address causes
* the proxy to terminate ( no core ) if the client breaks the connection during
* the response . Seen on 1.1 .8 pre4 , but never reproduced . May not be related to
* the snprintf ( ) bug since requests were simple ( GET / HTTP / 1.0 ) , but may be
* related to missing setsid ( ) ( fixed in 1.1 .15 )
* - a proxy with an invalid config will prevent the startup even if disabled .
*
* ChangeLog has moved to the CHANGELOG file .
*
*/
2015-12-08 16:43:09 -05:00
# define _GNU_SOURCE
2006-06-25 20:48:02 -04:00
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <ctype.h>
2016-05-13 17:52:56 -04:00
# include <dirent.h>
# include <sys/stat.h>
2006-06-25 20:48:02 -04:00
# include <sys/time.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/tcp.h>
# include <netinet/in.h>
# include <arpa/inet.h>
2017-04-05 16:33:04 -04:00
# include <net/if.h>
2006-06-25 20:48:02 -04:00
# include <netdb.h>
# include <fcntl.h>
# include <errno.h>
# include <signal.h>
# include <stdarg.h>
# include <sys/resource.h>
2013-02-12 04:53:52 -05:00
# include <sys/wait.h>
2006-06-25 20:48:02 -04:00
# include <time.h>
# include <syslog.h>
BUG/MEDIUM: remove supplementary groups when changing gid
Without it, haproxy will retain the group membership of root, which may
give more access than intended to the process. For example, haproxy would
still be in the wheel group on Fedora 18, as seen with :
# haproxy -f /etc/haproxy/haproxy.cfg
# ps a -o pid,user,group,command | grep hapr
3545 haproxy haproxy haproxy -f /etc/haproxy/haproxy.cfg
4356 root root grep --color=auto hapr
# grep Group /proc/3545/status
Groups: 0 1 2 3 4 6 10
# getent group wheel
wheel:x:10:root,misc
[WT: The issue has been investigated by independent security research team
and realized by itself not being able to allow security exploitation.
Additionally, dropping groups is not allowed to unprivileged users,
though this mode of deployment is quite common. Thus a warning is
emitted in this case to inform the user. The fix could be backported
into all supported versions as the issue has always been there. ]
2013-01-12 12:35:19 -05:00
# include <grp.h>
2012-11-16 10:12:27 -05:00
# ifdef USE_CPU_AFFINITY
# include <sched.h>
2018-11-12 11:22:19 -05:00
# if defined(__FreeBSD__) || defined(__DragonFly__)
2015-09-17 15:26:40 -04:00
# include <sys/param.h>
2018-11-12 11:22:19 -05:00
# ifdef __FreeBSD__
2015-09-17 15:26:40 -04:00
# include <sys/cpuset.h>
2018-11-12 11:22:19 -05:00
# endif
2017-11-29 06:02:32 -05:00
# include <pthread_np.h>
2015-09-17 15:26:40 -04:00
# endif
2012-11-16 10:12:27 -05:00
# endif
2006-06-25 20:48:02 -04:00
# ifdef DEBUG_FULL
# include <assert.h>
# endif
2017-11-20 09:58:35 -05:00
# if defined(USE_SYSTEMD)
# include <systemd/sd-daemon.h>
# endif
2006-06-25 20:48:02 -04:00
2006-06-29 11:53:05 -04:00
# include <common/base64.h>
# include <common/cfgparse.h>
2012-08-24 13:22:53 -04:00
# include <common/chunk.h>
2006-06-29 11:53:05 -04:00
# include <common/compat.h>
# include <common/config.h>
# include <common/defaults.h>
2007-10-28 06:14:07 -04:00
# include <common/errors.h>
2018-11-25 12:43:29 -05:00
# include <common/initcall.h>
2006-06-29 11:53:05 -04:00
# include <common/memory.h>
# include <common/mini-clist.h>
2014-11-17 09:11:45 -05:00
# include <common/namespace.h>
2006-06-29 11:53:05 -04:00
# include <common/regex.h>
# include <common/standard.h>
# include <common/time.h>
# include <common/uri_auth.h>
# include <common/version.h>
2017-08-29 09:37:10 -04:00
# include <common/hathreads.h>
2006-06-25 20:48:02 -04:00
# include <types/capture.h>
2018-10-26 08:47:30 -04:00
# include <types/cli.h>
MAJOR: filters: Add filters support
This patch adds the support of filters in HAProxy. The main idea is to have a
way to "easely" extend HAProxy by adding some "modules", called filters, that
will be able to change HAProxy behavior in a programmatic way.
To do so, many entry points has been added in code to let filters to hook up to
different steps of the processing. A filter must define a flt_ops sutrctures
(see include/types/filters.h for details). This structure contains all available
callbacks that a filter can define:
struct flt_ops {
/*
* Callbacks to manage the filter lifecycle
*/
int (*init) (struct proxy *p);
void (*deinit)(struct proxy *p);
int (*check) (struct proxy *p);
/*
* Stream callbacks
*/
void (*stream_start) (struct stream *s);
void (*stream_accept) (struct stream *s);
void (*session_establish)(struct stream *s);
void (*stream_stop) (struct stream *s);
/*
* HTTP callbacks
*/
int (*http_start) (struct stream *s, struct http_msg *msg);
int (*http_start_body) (struct stream *s, struct http_msg *msg);
int (*http_start_chunk) (struct stream *s, struct http_msg *msg);
int (*http_data) (struct stream *s, struct http_msg *msg);
int (*http_last_chunk) (struct stream *s, struct http_msg *msg);
int (*http_end_chunk) (struct stream *s, struct http_msg *msg);
int (*http_chunk_trailers)(struct stream *s, struct http_msg *msg);
int (*http_end_body) (struct stream *s, struct http_msg *msg);
void (*http_end) (struct stream *s, struct http_msg *msg);
void (*http_reset) (struct stream *s, struct http_msg *msg);
int (*http_pre_process) (struct stream *s, struct http_msg *msg);
int (*http_post_process) (struct stream *s, struct http_msg *msg);
void (*http_reply) (struct stream *s, short status,
const struct chunk *msg);
};
To declare and use a filter, in the configuration, the "filter" keyword must be
used in a listener/frontend section:
frontend test
...
filter <FILTER-NAME> [OPTIONS...]
The filter referenced by the <FILTER-NAME> must declare a configuration parser
on its own name to fill flt_ops and filter_conf field in the proxy's
structure. An exemple will be provided later to make it perfectly clear.
For now, filters cannot be used in backend section. But this is only a matter of
time. Documentation will also be added later. This is the first commit of a long
list about filters.
It is possible to have several filters on the same listener/frontend. These
filters are stored in an array of at most MAX_FILTERS elements (define in
include/types/filters.h). Again, this will be replaced later by a list of
filters.
The filter API has been highly refactored. Main changes are:
* Now, HA supports an infinite number of filters per proxy. To do so, filters
are stored in list.
* Because filters are stored in list, filters state has been moved from the
channel structure to the filter structure. This is cleaner because there is no
more info about filters in channel structure.
* It is possible to defined filters on backends only. For such filters,
stream_start/stream_stop callbacks are not called. Of course, it is possible
to mix frontend and backend filters.
* Now, TCP streams are also filtered. All callbacks without the 'http_' prefix
are called for all kind of streams. In addition, 2 new callbacks were added to
filter data exchanged through a TCP stream:
- tcp_data: it is called when new data are available or when old unprocessed
data are still waiting.
- tcp_forward_data: it is called when some data can be consumed.
* New callbacks attached to channel were added:
- channel_start_analyze: it is called when a filter is ready to process data
exchanged through a channel. 2 new analyzers (a frontend and a backend)
are attached to channels to call this callback. For a frontend filter, it
is called before any other analyzer. For a backend filter, it is called
when a backend is attached to a stream. So some processing cannot be
filtered in that case.
- channel_analyze: it is called before each analyzer attached to a channel,
expects analyzers responsible for data sending.
- channel_end_analyze: it is called when all other analyzers have finished
their processing. A new analyzers is attached to channels to call this
callback. For a TCP stream, this is always the last one called. For a HTTP
one, the callback is called when a request/response ends, so it is called
one time for each request/response.
* 'session_established' callback has been removed. Everything that is done in
this callback can be handled by 'channel_start_analyze' on the response
channel.
* 'http_pre_process' and 'http_post_process' callbacks have been replaced by
'channel_analyze'.
* 'http_start' callback has been replaced by 'http_headers'. This new one is
called just before headers sending and parsing of the body.
* 'http_end' callback has been replaced by 'channel_end_analyze'.
* It is possible to set a forwarder for TCP channels. It was already possible to
do it for HTTP ones.
* Forwarders can partially consumed forwardable data. For this reason a new
HTTP message state was added before HTTP_MSG_DONE : HTTP_MSG_ENDING.
Now all filters can define corresponding callbacks (http_forward_data
and tcp_forward_data). Each filter owns 2 offsets relative to buf->p, next and
forward, to track, respectively, input data already parsed but not forwarded yet
by the filter and parsed data considered as forwarded by the filter. A any time,
we have the warranty that a filter cannot parse or forward more input than
previous ones. And, of course, it cannot forward more input than it has
parsed. 2 macros has been added to retrieve these offets: FLT_NXT and FLT_FWD.
In addition, 2 functions has been added to change the 'next size' and the
'forward size' of a filter. When a filter parses input data, it can alter these
data, so the size of these data can vary. This action has an effet on all
previous filters that must be handled. To do so, the function
'filter_change_next_size' must be called, passing the size variation. In the
same spirit, if a filter alter forwarded data, it must call the function
'filter_change_forward_size'. 'filter_change_next_size' can be called in
'http_data' and 'tcp_data' callbacks and only these ones. And
'filter_change_forward_size' can be called in 'http_forward_data' and
'tcp_forward_data' callbacks and only these ones. The data changes are the
filter responsability, but with some limitation. It must not change already
parsed/forwarded data or data that previous filters have not parsed/forwarded
yet.
Because filters can be used on backends, when we the backend is set for a
stream, we add filters defined for this backend in the filter list of the
stream. But we must only do that when the backend and the frontend of the stream
are not the same. Else same filters are added a second time leading to undefined
behavior.
The HTTP compression code had to be moved.
So it simplifies http_response_forward_body function. To do so, the way the data
are forwarded has changed. Now, a filter (and only one) can forward data. In a
commit to come, this limitation will be removed to let all filters take part to
data forwarding. There are 2 new functions that filters should use to deal with
this feature:
* flt_set_http_data_forwarder: This function sets the filter (using its id)
that will forward data for the specified HTTP message. It is possible if it
was not already set by another filter _AND_ if no data was yet forwarded
(msg->msg_state <= HTTP_MSG_BODY). It returns -1 if an error occurs.
* flt_http_data_forwarder: This function returns the filter id that will
forward data for the specified HTTP message. If there is no forwarder set, it
returns -1.
When an HTTP data forwarder is set for the response, the HTTP compression is
disabled. Of course, this is not definitive.
2015-04-30 05:48:27 -04:00
# include <types/filters.h>
2006-06-25 20:48:02 -04:00
# include <types/global.h>
2011-07-15 00:14:09 -04:00
# include <types/acl.h>
2011-09-07 12:00:47 -04:00
# include <types/peers.h>
2006-06-25 20:48:02 -04:00
2007-06-16 18:36:03 -04:00
# include <proto/acl.h>
2018-11-22 02:31:09 -05:00
# include <proto/activity.h>
2012-10-19 13:49:09 -04:00
# include <proto/arg.h>
2015-04-19 03:59:31 -04:00
# include <proto/auth.h>
2006-06-25 20:48:02 -04:00
# include <proto/backend.h>
2012-08-24 13:22:53 -04:00
# include <proto/channel.h>
2018-10-26 08:47:30 -04:00
# include <proto/cli.h>
2012-10-26 14:10:28 -04:00
# include <proto/connection.h>
2006-06-25 20:48:02 -04:00
# include <proto/fd.h>
MAJOR: filters: Add filters support
This patch adds the support of filters in HAProxy. The main idea is to have a
way to "easely" extend HAProxy by adding some "modules", called filters, that
will be able to change HAProxy behavior in a programmatic way.
To do so, many entry points has been added in code to let filters to hook up to
different steps of the processing. A filter must define a flt_ops sutrctures
(see include/types/filters.h for details). This structure contains all available
callbacks that a filter can define:
struct flt_ops {
/*
* Callbacks to manage the filter lifecycle
*/
int (*init) (struct proxy *p);
void (*deinit)(struct proxy *p);
int (*check) (struct proxy *p);
/*
* Stream callbacks
*/
void (*stream_start) (struct stream *s);
void (*stream_accept) (struct stream *s);
void (*session_establish)(struct stream *s);
void (*stream_stop) (struct stream *s);
/*
* HTTP callbacks
*/
int (*http_start) (struct stream *s, struct http_msg *msg);
int (*http_start_body) (struct stream *s, struct http_msg *msg);
int (*http_start_chunk) (struct stream *s, struct http_msg *msg);
int (*http_data) (struct stream *s, struct http_msg *msg);
int (*http_last_chunk) (struct stream *s, struct http_msg *msg);
int (*http_end_chunk) (struct stream *s, struct http_msg *msg);
int (*http_chunk_trailers)(struct stream *s, struct http_msg *msg);
int (*http_end_body) (struct stream *s, struct http_msg *msg);
void (*http_end) (struct stream *s, struct http_msg *msg);
void (*http_reset) (struct stream *s, struct http_msg *msg);
int (*http_pre_process) (struct stream *s, struct http_msg *msg);
int (*http_post_process) (struct stream *s, struct http_msg *msg);
void (*http_reply) (struct stream *s, short status,
const struct chunk *msg);
};
To declare and use a filter, in the configuration, the "filter" keyword must be
used in a listener/frontend section:
frontend test
...
filter <FILTER-NAME> [OPTIONS...]
The filter referenced by the <FILTER-NAME> must declare a configuration parser
on its own name to fill flt_ops and filter_conf field in the proxy's
structure. An exemple will be provided later to make it perfectly clear.
For now, filters cannot be used in backend section. But this is only a matter of
time. Documentation will also be added later. This is the first commit of a long
list about filters.
It is possible to have several filters on the same listener/frontend. These
filters are stored in an array of at most MAX_FILTERS elements (define in
include/types/filters.h). Again, this will be replaced later by a list of
filters.
The filter API has been highly refactored. Main changes are:
* Now, HA supports an infinite number of filters per proxy. To do so, filters
are stored in list.
* Because filters are stored in list, filters state has been moved from the
channel structure to the filter structure. This is cleaner because there is no
more info about filters in channel structure.
* It is possible to defined filters on backends only. For such filters,
stream_start/stream_stop callbacks are not called. Of course, it is possible
to mix frontend and backend filters.
* Now, TCP streams are also filtered. All callbacks without the 'http_' prefix
are called for all kind of streams. In addition, 2 new callbacks were added to
filter data exchanged through a TCP stream:
- tcp_data: it is called when new data are available or when old unprocessed
data are still waiting.
- tcp_forward_data: it is called when some data can be consumed.
* New callbacks attached to channel were added:
- channel_start_analyze: it is called when a filter is ready to process data
exchanged through a channel. 2 new analyzers (a frontend and a backend)
are attached to channels to call this callback. For a frontend filter, it
is called before any other analyzer. For a backend filter, it is called
when a backend is attached to a stream. So some processing cannot be
filtered in that case.
- channel_analyze: it is called before each analyzer attached to a channel,
expects analyzers responsible for data sending.
- channel_end_analyze: it is called when all other analyzers have finished
their processing. A new analyzers is attached to channels to call this
callback. For a TCP stream, this is always the last one called. For a HTTP
one, the callback is called when a request/response ends, so it is called
one time for each request/response.
* 'session_established' callback has been removed. Everything that is done in
this callback can be handled by 'channel_start_analyze' on the response
channel.
* 'http_pre_process' and 'http_post_process' callbacks have been replaced by
'channel_analyze'.
* 'http_start' callback has been replaced by 'http_headers'. This new one is
called just before headers sending and parsing of the body.
* 'http_end' callback has been replaced by 'channel_end_analyze'.
* It is possible to set a forwarder for TCP channels. It was already possible to
do it for HTTP ones.
* Forwarders can partially consumed forwardable data. For this reason a new
HTTP message state was added before HTTP_MSG_DONE : HTTP_MSG_ENDING.
Now all filters can define corresponding callbacks (http_forward_data
and tcp_forward_data). Each filter owns 2 offsets relative to buf->p, next and
forward, to track, respectively, input data already parsed but not forwarded yet
by the filter and parsed data considered as forwarded by the filter. A any time,
we have the warranty that a filter cannot parse or forward more input than
previous ones. And, of course, it cannot forward more input than it has
parsed. 2 macros has been added to retrieve these offets: FLT_NXT and FLT_FWD.
In addition, 2 functions has been added to change the 'next size' and the
'forward size' of a filter. When a filter parses input data, it can alter these
data, so the size of these data can vary. This action has an effet on all
previous filters that must be handled. To do so, the function
'filter_change_next_size' must be called, passing the size variation. In the
same spirit, if a filter alter forwarded data, it must call the function
'filter_change_forward_size'. 'filter_change_next_size' can be called in
'http_data' and 'tcp_data' callbacks and only these ones. And
'filter_change_forward_size' can be called in 'http_forward_data' and
'tcp_forward_data' callbacks and only these ones. The data changes are the
filter responsability, but with some limitation. It must not change already
parsed/forwarded data or data that previous filters have not parsed/forwarded
yet.
Because filters can be used on backends, when we the backend is set for a
stream, we add filters defined for this backend in the filter list of the
stream. But we must only do that when the backend and the frontend of the stream
are not the same. Else same filters are added a second time leading to undefined
behavior.
The HTTP compression code had to be moved.
So it simplifies http_response_forward_body function. To do so, the way the data
are forwarded has changed. Now, a filter (and only one) can forward data. In a
commit to come, this limitation will be removed to let all filters take part to
data forwarding. There are 2 new functions that filters should use to deal with
this feature:
* flt_set_http_data_forwarder: This function sets the filter (using its id)
that will forward data for the specified HTTP message. It is possible if it
was not already set by another filter _AND_ if no data was yet forwarded
(msg->msg_state <= HTTP_MSG_BODY). It returns -1 if an error occurs.
* flt_http_data_forwarder: This function returns the filter id that will
forward data for the specified HTTP message. If there is no forwarder set, it
returns -1.
When an HTTP data forwarder is set for the response, the HTTP compression is
disabled. Of course, this is not definitive.
2015-04-30 05:48:27 -04:00
# include <proto/filters.h>
2011-10-24 12:15:04 -04:00
# include <proto/hdr_idx.h>
2015-01-23 08:06:13 -05:00
# include <proto/hlua.h>
2018-10-02 10:43:32 -04:00
# include <proto/http_rules.h>
2012-09-12 16:58:11 -04:00
# include <proto/listener.h>
2006-06-25 20:48:02 -04:00
# include <proto/log.h>
2014-03-11 09:29:22 -04:00
# include <proto/pattern.h>
2012-09-12 16:58:11 -04:00
# include <proto/protocol.h>
2006-12-24 11:47:20 -05:00
# include <proto/proto_http.h>
2006-06-25 20:48:02 -04:00
# include <proto/proxy.h>
# include <proto/queue.h>
# include <proto/server.h>
2015-04-03 07:53:24 -04:00
# include <proto/session.h>
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
# include <proto/stream.h>
2009-05-10 03:01:21 -04:00
# include <proto/signal.h>
2006-06-25 20:48:02 -04:00
# include <proto/task.h>
2015-04-13 17:40:55 -04:00
# include <proto/dns.h>
2016-11-09 05:36:17 -05:00
# include <proto/vars.h>
2017-08-16 09:35:19 -04:00
# ifdef USE_OPENSSL
2017-01-20 20:10:18 -05:00
# include <proto/ssl_sock.h>
2017-08-16 09:35:19 -04:00
# endif
2006-06-25 20:48:02 -04:00
2010-01-03 15:12:30 -05:00
/* list of config files */
static struct list cfg_cfgfiles = LIST_HEAD_INIT ( cfg_cfgfiles ) ;
2006-06-25 20:48:02 -04:00
int pid ; /* current process id */
2007-11-26 10:13:36 -05:00
int relative_pid = 1 ; /* process id starting at 1 */
2017-11-10 13:08:14 -05:00
unsigned long pid_bit = 1 ; /* bit corresponding to the process id */
2006-06-25 20:48:02 -04:00
2018-07-26 11:55:11 -04:00
volatile unsigned long sleeping_thread_mask ; /* Threads that are about to sleep in poll() */
2006-06-25 20:48:02 -04:00
/* global options */
struct global global = {
2017-03-23 17:44:13 -04:00
. hard_stop_after = TICK_ETERNITY ,
2012-11-15 11:38:15 -05:00
. nbproc = 1 ,
2017-08-29 09:37:10 -04:00
. nbthread = 1 ,
2012-04-05 12:02:55 -04:00
. req_count = 0 ,
2011-10-12 11:50:54 -04:00
. logsrvs = LIST_HEAD_INIT ( global . logsrvs ) ,
2012-11-07 10:12:57 -05:00
. maxzlibmem = 0 ,
2012-11-09 11:05:39 -05:00
. comp_rate_lim = 0 ,
2014-01-29 06:24:34 -05:00
. ssl_server_verify = SSL_SERVER_VERIFY_REQUIRED ,
2010-10-22 11:59:25 -04:00
. unix_bind = {
. ux = {
. uid = - 1 ,
. gid = - 1 ,
. mode = 0 ,
}
} ,
2009-08-17 01:23:33 -04:00
. tune = {
2018-12-12 00:19:42 -05:00
. bufsize = ( BUFSIZE + 2 * sizeof ( void * ) - 1 ) & - ( 2 * sizeof ( void * ) ) ,
2015-09-28 07:53:23 -04:00
. maxrewrite = - 1 ,
2018-12-12 00:19:42 -05:00
. chksize = ( BUFSIZE + 2 * sizeof ( void * ) - 1 ) & - ( 2 * sizeof ( void * ) ) ,
MAJOR: session: only wake up as many sessions as available buffers permit
We've already experimented with three wake up algorithms when releasing
buffers : the first naive one used to wake up far too many sessions,
causing many of them not to get any buffer. The second approach which
was still in use prior to this patch consisted in waking up either 1
or 2 sessions depending on the number of FDs we had released. And this
was still inaccurate. The third one tried to cover the accuracy issues
of the second and took into consideration the number of FDs the sessions
would be willing to use, but most of the time we ended up waking up too
many of them for nothing, or deadlocking by lack of buffers.
This patch completely removes the need to allocate two buffers at once.
Instead it splits allocations into critical and non-critical ones and
implements a reserve in the pool for this. The deadlock situation happens
when all buffers are be allocated for requests pending in a maxconn-limited
server queue, because then there's no more way to allocate buffers for
responses, and these responses are critical to release the servers's
connection in order to release the pending requests. In fact maxconn on
a server creates a dependence between sessions and particularly between
oldest session's responses and latest session's requests. Thus, it is
mandatory to get a free buffer for a response in order to release a
server connection which will permit to release a request buffer.
Since we definitely have non-symmetrical buffers, we need to implement
this logic in the buffer allocation mechanism. What this commit does is
implement a reserve of buffers which can only be allocated for responses
and that will never be allocated for requests. This is made possible by
the requester indicating how much margin it wants to leave after the
allocation succeeds. Thus it is a cooperative allocation mechanism : the
requester (process_session() in general) prefers not to get a buffer in
order to respect other's need for response buffers. The session management
code always knows if a buffer will be used for requests or responses, so
that is not difficult :
- either there's an applet on the initiator side and we really need
the request buffer (since currently the applet is called in the
context of the session)
- or we have a connection and we really need the response buffer (in
order to support building and sending an error message back)
This reserve ensures that we don't take all allocatable buffers for
requests waiting in a queue. The downside is that all the extra buffers
are really allocated to ensure they can be allocated. But with small
values it is not an issue.
With this change, we don't observe any more deadlocks even when running
with maxconn 1 on a server under severely constrained memory conditions.
The code becomes a bit tricky, it relies on the scheduler's run queue to
estimate how many sessions are already expected to run so that it doesn't
wake up everyone with too few resources. A better solution would probably
consist in having two queues, one for urgent requests and one for normal
requests. A failed allocation for a session dealing with an error, a
connection event, or the need for a response (or request when there's an
applet on the left) would go to the urgent request queue, while other
requests would go to the other queue. Urgent requests would be served
from 1 entry in the pool, while the regular ones would be served only
according to the reserve. Despite not yet having this, it works
remarkably well.
This mechanism is quite efficient, we don't perform too many wake up calls
anymore. For 1 million sessions elapsed during massive memory contention,
we observe about 4.5M calls to process_session() compared to 4.0M without
memory constraints. Previously we used to observe up to 16M calls, which
rougly means 12M failures.
During a test run under high memory constraints (limit enforced to 27 MB
instead of the 58 MB normally needed), performance used to drop by 53% prior
to this patch. Now with this patch instead it *increases* by about 1.5%.
The best effect of this change is that by limiting the memory usage to about
2/3 to 3/4 of what is needed by default, it's possible to increase performance
by up to about 18% mainly due to the fact that pools are reused more often
and remain hot in the CPU cache (observed on regular HTTP traffic with 20k
objects, buffers.limit = maxconn/10, buffers.reserve = limit/2).
Below is an example of scenario which used to cause a deadlock previously :
- connection is received
- two buffers are allocated in process_session() then released
- one is allocated when receiving an HTTP request
- the second buffer is allocated then released in process_session()
for request parsing then connection establishment.
- poll() says we can send, so the request buffer is sent and released
- process session gets notified that the connection is now established
and allocates two buffers then releases them
- all other sessions do the same till one cannot get the request buffer
without hitting the margin
- and now the server responds. stream_interface allocates the response
buffer and manages to get it since it's higher priority being for a
response.
- but process_session() cannot allocate the request buffer anymore
=> We could end up with all buffers used by responses so that none may
be allocated for a request in process_session().
When the applet processing leaves the session context, the test will have
to be changed so that we always allocate a response buffer regardless of
the left side (eg: H2->H1 gateway). A final improvement would consists in
being able to only retry the failed I/O operation without waking up a
task, but to date all experiments to achieve this have proven not to be
reliable enough.
2014-11-26 19:11:56 -05:00
. reserved_bufs = RESERVED_BUFS ,
2015-04-29 10:24:50 -04:00
. pattern_cache = DEFAULT_PAT_LRU_SIZE ,
2012-09-03 06:10:29 -04:00
# ifdef USE_OPENSSL
2012-11-14 05:32:56 -05:00
. sslcachesize = SSLCACHESIZE ,
2012-11-07 10:54:34 -05:00
# endif
2012-11-09 06:33:10 -05:00
. comp_maxlevel = 1 ,
2014-02-12 10:35:14 -05:00
# ifdef DEFAULT_IDLE_TIMER
. idle_timer = DEFAULT_IDLE_TIMER ,
# else
. idle_timer = 1000 , /* 1 second */
# endif
2009-08-17 01:23:33 -04:00
} ,
2012-10-05 09:47:31 -04:00
# ifdef USE_OPENSSL
# ifdef DEFAULT_MAXSSLCONN
2012-09-06 05:58:37 -04:00
. maxsslconn = DEFAULT_MAXSSLCONN ,
2012-10-05 09:47:31 -04:00
# endif
2012-09-06 05:58:37 -04:00
# endif
2006-06-25 20:48:02 -04:00
/* others NULL OK */
} ;
/*********************************************************************/
int stopping ; /* non zero means stopping in progress */
2017-03-23 17:44:13 -04:00
int killed ; /* non zero means a hard-stop is triggered */
2010-08-31 09:39:26 -04:00
int jobs = 0 ; /* number of active jobs (conns, listeners, active tasks, ...) */
2018-11-16 10:57:20 -05:00
int unstoppable_jobs = 0 ; /* number of active jobs that can't be stopped during a soft stop */
2018-11-05 10:31:22 -05:00
int active_peers = 0 ; /* number of active peers (connection attempts and connected) */
2018-11-05 11:12:27 -05:00
int connected_peers = 0 ; /* number of connected peers (verified ones) */
2006-06-25 20:48:02 -04:00
/* Here we store informations about the pids of the processes we may pause
* or kill . We will send them a signal every 10 ms until we can bind to all
* our ports . With 200 retries , that ' s about 2 seconds .
*/
# define MAX_START_RETRIES 200
static int * oldpids = NULL ;
static int oldpids_sig ; /* use USR1 or TERM */
2017-04-05 16:33:04 -04:00
/* Path to the unix socket we use to retrieve listener sockets from the old process */
static const char * old_unixsocket ;
2017-06-01 11:38:53 -04:00
static char * cur_unixsocket = NULL ;
2017-06-01 11:38:52 -04:00
int atexit_flag = 0 ;
2018-11-06 11:37:14 -05:00
static int exitcode = - 1 ;
2010-08-25 06:58:59 -04:00
int nb_oldpids = 0 ;
2006-06-25 20:48:02 -04:00
const int zero = 0 ;
const int one = 1 ;
2007-10-11 14:48:58 -04:00
const struct linger nolinger = { . l_onoff = 1 , . l_linger = 0 } ;
2006-06-25 20:48:02 -04:00
2010-03-12 15:58:54 -05:00
char hostname [ MAX_HOSTNAME_LEN ] ;
2010-09-23 12:30:22 -04:00
char localpeer [ MAX_HOSTNAME_LEN ] ;
2006-06-25 20:48:02 -04:00
2013-12-13 09:14:55 -05:00
/* used from everywhere just to drain results we don't want to read and which
* recent versions of gcc increasingly and annoyingly complain about .
*/
int shut_your_big_mouth_gcc_int = 0 ;
2017-06-01 11:38:51 -04:00
int * children = NULL ; /* store PIDs of children in master workers mode */
static char * * next_argv = NULL ;
2018-09-11 04:06:26 -04:00
struct list proc_list = LIST_HEAD_INIT ( proc_list ) ;
int master = 0 ; /* 1 if in master, 0 if in child */
2018-11-19 12:46:18 -05:00
struct mworker_proc * proc_self = NULL ;
2018-09-11 04:06:26 -04:00
2011-07-24 16:58:00 -04:00
/* list of the temporarily limited listeners because of lack of resource */
struct list global_listener_queue = LIST_HEAD_INIT ( global_listener_queue ) ;
2011-08-01 14:57:55 -04:00
struct task * global_listener_queue_task ;
2018-05-25 08:04:04 -04:00
static struct task * manage_global_listener_queue ( struct task * t , void * context , unsigned short state ) ;
2006-06-25 20:48:02 -04:00
2018-09-11 04:06:18 -04:00
static void * run_thread_poll_loop ( void * data ) ;
2014-04-28 16:27:06 -04:00
/* bitfield of a few warnings to emit just once (WARN_*) */
unsigned int warned = 0 ;
2018-10-26 08:47:36 -04:00
/* master CLI configuration (-S flag) */
struct list mworker_cli_conf = LIST_HEAD_INIT ( mworker_cli_conf ) ;
2016-12-21 12:43:10 -05:00
/* These are strings to be reported in the output of "haproxy -vv". They may
* either be constants ( in which case must_free must be zero ) or dynamically
* allocated strings to pass to free ( ) on exit , and in this case must_free
* must be non - zero .
*/
struct list build_opts_list = LIST_HEAD_INIT ( build_opts_list ) ;
struct build_opts_str {
struct list list ;
const char * str ;
int must_free ;
} ;
2016-12-21 13:57:00 -05:00
/* These functions are called just after the point where the program exits
* after a config validity check , so they are generally suited for resource
* allocation and slow initializations that should be skipped during basic
* config checks . The functions must return 0 on success , or a combination
* of ERR_ * flags ( ERR_WARN , ERR_ABORT , ERR_FATAL , . . . ) . The 2 latter cause
* and immediate exit , so the function must have emitted any useful error .
*/
struct list post_check_list = LIST_HEAD_INIT ( post_check_list ) ;
struct post_check_fct {
struct list list ;
int ( * fct ) ( ) ;
} ;
2016-12-21 14:46:26 -05:00
/* These functions are called when freeing the global sections at the end
* of deinit , after everything is stopped . They don ' t return anything , and
* they work in best effort mode as their sole goal is to make valgrind
* mostly happy .
*/
struct list post_deinit_list = LIST_HEAD_INIT ( post_deinit_list ) ;
struct post_deinit_fct {
struct list list ;
void ( * fct ) ( ) ;
} ;
2017-07-25 10:52:58 -04:00
/* These functions are called for each thread just after the thread creation
* and before running the scheduler . They should be used to do per - thread
* initializations . They must return 0 if an error occurred . */
struct list per_thread_init_list = LIST_HEAD_INIT ( per_thread_init_list ) ;
struct per_thread_init_fct {
struct list list ;
int ( * fct ) ( ) ;
} ;
/* These functions are called for each thread just after the scheduler loop and
* before exiting the thread . They don ' t return anything and , as for post - deinit
* functions , they work in best effort mode as their sole goal is to make
* valgrind mostly happy . */
struct list per_thread_deinit_list = LIST_HEAD_INIT ( per_thread_deinit_list ) ;
struct per_thread_deinit_fct {
struct list list ;
void ( * fct ) ( ) ;
} ;
2006-06-25 20:48:02 -04:00
/*********************************************************************/
/* general purpose functions ***************************************/
/*********************************************************************/
2016-12-21 12:43:10 -05:00
/* used to register some build option strings at boot. Set must_free to
* non - zero if the string must be freed upon exit .
*/
void hap_register_build_opts ( const char * str , int must_free )
{
struct build_opts_str * b ;
b = calloc ( 1 , sizeof ( * b ) ) ;
if ( ! b ) {
fprintf ( stderr , " out of memory \n " ) ;
exit ( 1 ) ;
}
b - > str = str ;
b - > must_free = must_free ;
LIST_ADDQ ( & build_opts_list , & b - > list ) ;
}
2016-12-21 13:57:00 -05:00
/* used to register some initialization functions to call after the checks. */
void hap_register_post_check ( int ( * fct ) ( ) )
{
struct post_check_fct * b ;
b = calloc ( 1 , sizeof ( * b ) ) ;
if ( ! b ) {
fprintf ( stderr , " out of memory \n " ) ;
exit ( 1 ) ;
}
b - > fct = fct ;
LIST_ADDQ ( & post_check_list , & b - > list ) ;
}
2016-12-21 14:46:26 -05:00
/* used to register some de-initialization functions to call after everything
* has stopped .
*/
void hap_register_post_deinit ( void ( * fct ) ( ) )
{
struct post_deinit_fct * b ;
b = calloc ( 1 , sizeof ( * b ) ) ;
if ( ! b ) {
fprintf ( stderr , " out of memory \n " ) ;
exit ( 1 ) ;
}
b - > fct = fct ;
LIST_ADDQ ( & post_deinit_list , & b - > list ) ;
}
2017-07-25 10:52:58 -04:00
/* used to register some initialization functions to call for each thread. */
void hap_register_per_thread_init ( int ( * fct ) ( ) )
{
struct per_thread_init_fct * b ;
b = calloc ( 1 , sizeof ( * b ) ) ;
if ( ! b ) {
fprintf ( stderr , " out of memory \n " ) ;
exit ( 1 ) ;
}
b - > fct = fct ;
LIST_ADDQ ( & per_thread_init_list , & b - > list ) ;
}
/* used to register some de-initialization functions to call for each thread. */
void hap_register_per_thread_deinit ( void ( * fct ) ( ) )
{
struct per_thread_deinit_fct * b ;
b = calloc ( 1 , sizeof ( * b ) ) ;
if ( ! b ) {
fprintf ( stderr , " out of memory \n " ) ;
exit ( 1 ) ;
}
b - > fct = fct ;
LIST_ADDQ ( & per_thread_deinit_list , & b - > list ) ;
}
2016-12-21 12:19:57 -05:00
static void display_version ( )
2006-06-25 20:48:02 -04:00
{
printf ( " HA-Proxy version " HAPROXY_VERSION " " HAPROXY_DATE " \n " ) ;
[RELEASE] Released version 1.9-dev1
Released version 1.9-dev1 with the following main changes :
- BUG/MEDIUM: kqueue: Don't bother closing the kqueue after fork.
- DOC: cache: update sections and fix some typos
- BUILD/MINOR: deviceatlas: enable thread support
- BUG/MEDIUM: tcp-check: Don't lock the server in tcpcheck_main
- BUG/MEDIUM: ssl: don't allocate shctx several time
- BUG/MEDIUM: cache: bad computation of the remaining size
- BUILD: checks: don't include server.h
- BUG/MEDIUM: stream: fix session leak on applet-initiated connections
- BUILD/MINOR: haproxy : FreeBSD/cpu affinity needs pthread_np header
- BUILD/MINOR: Makefile : enabling USE_CPU_AFFINITY
- BUG/MINOR: ssl: CO_FL_EARLY_DATA removal is managed by stream
- BUG/MEDIUM: threads/peers: decrement, not increment jobs on quitting
- BUG/MEDIUM: h2: don't report an error after parsing a 100-continue response
- BUG/MEDIUM: peers: fix some track counter rules dont register entries for sync.
- BUG/MAJOR: thread/peers: fix deadlock on peers sync.
- BUILD/MINOR: haproxy: compiling config cpu parsing handling when needed
- MINOR: config: report when "monitor fail" rules are misplaced
- BUG/MINOR: mworker: fix validity check for the pipe FDs
- BUG/MINOR: mworker: detach from tty when in daemon mode
- MINOR: threads: Fix pthread_setaffinity_np on FreeBSD.
- BUG/MAJOR: thread: Be sure to request a sync between threads only once at a time
- BUILD: Fix LDFLAGS vs. LIBS re linking order in various makefiles
- BUG/MEDIUM: checks: Be sure we have a mux if we created a cs.
- BUG/MINOR: hpack: fix debugging output of pseudo header names
- BUG/MINOR: hpack: must reject huffman literals padded with more than 7 bits
- BUG/MINOR: hpack: reject invalid header index
- BUG/MINOR: hpack: dynamic table size updates are only allowed before headers
- BUG/MAJOR: h2: correctly check the request length when building an H1 request
- BUG/MINOR: h2: immediately close if receiving GOAWAY after the last stream
- BUG/MINOR: h2: try to abort closed streams as soon as possible
- BUG/MINOR: h2: ":path" must not be empty
- BUG/MINOR: h2: fix a typo causing PING/ACK to be responded to
- BUG/MINOR: h2: the TE header if present may only contain trailers
- BUG/MEDIUM: h2: enforce the per-connection stream limit
- BUG/MINOR: h2: do not accept SETTINGS_ENABLE_PUSH other than 0 or 1
- BUG/MINOR: h2: reject incorrect stream dependencies on HEADERS frame
- BUG/MINOR: h2: properly check PRIORITY frames
- BUG/MINOR: h2: reject response pseudo-headers from requests
- BUG/MEDIUM: h2: remove connection-specific headers from request
- BUG/MEDIUM: h2: do not accept upper case letters in request header names
- BUG/MINOR: h2: use the H2_F_DATA_* macros for DATA frames
- BUG/MINOR: action: Don't check http capture rules when no id is defined
- BUG/MAJOR: hpack: don't pretend large headers fit in empty table
- BUG/MINOR: ssl: support tune.ssl.cachesize 0 again
- BUG/MEDIUM: mworker: also close peers sockets in the master
- BUG/MEDIUM: ssl engines: Fix async engines fds were not considered to fix fd limit automatically.
- BUG/MEDIUM: checks: a down server going to maint remains definitely stucked on down state.
- BUG/MEDIUM: peers: set NOLINGER on the outgoing stream interface
- BUG/MEDIUM: h2: fix handling of end of stream again
- MINOR: mworker: Update messages referencing exit-on-failure
- MINOR: mworker: Improve wording in `void mworker_wait()`
- CONTRIB: halog: Add help text for -s switch in halog program
- BUG/MEDIUM: email-alert: don't set server check status from a email-alert task
- BUG/MEDIUM: threads/vars: Fix deadlock in register_name
- MINOR: systemd: remove comment about HAPROXY_STATS_SOCKET
- DOC: notifications: add precisions about thread usage
- BUG/MEDIUM: lua/notification: memory leak
- MINOR: conn_stream: add new flag CS_FL_RCV_MORE to indicate pending data
- BUG/MEDIUM: stream-int: always set SI_FL_WAIT_ROOM on CS_FL_RCV_MORE
- BUG/MEDIUM: h2: automatically set CS_FL_RCV_MORE when the output buffer is full
- BUG/MEDIUM: h2: enable recv polling whenever demuxing is possible
- BUG/MEDIUM: h2: work around a connection API limitation
- BUG/MEDIUM: h2: debug incoming traffic in h2_wake()
- MINOR: h2: store the demux padding length in the h2c struct
- BUG/MEDIUM: h2: support uploading partial DATA frames
- MINOR: h2: don't demand that a DATA frame is complete before processing it
- BUG/MEDIUM: h2: don't switch the state to HREM before end of DATA frame
- BUG/MEDIUM: h2: don't close after the first DATA frame on tunnelled responses
- BUG/MEDIUM: http: don't disable lingering on requests with tunnelled responses
- BUG/MEDIUM: h2: fix stream limit enforcement
- BUG/MINOR: stream-int: don't try to receive again after receiving an EOS
- MINOR: sample: add len converter
- BUG: MAJOR: lb_map: server map calculation broken
- BUG: MINOR: http: don't check http-request capture id when len is provided
- MINOR: sample: rename the "len" converter to "length"
- BUG/MEDIUM: mworker: Set FD_CLOEXEC flag on log fd
- DOC/MINOR: intro: typo, wording, formatting fixes
- MINOR: netscaler: respect syntax
- MINOR: netscaler: remove the use of cip_magic only used once
- MINOR: netscaler: rename cip_len to clarify its uage
- BUG/MEDIUM: netscaler: use the appropriate IPv6 header size
- BUG/MAJOR: netscaler: address truncated CIP header detection
- MINOR: netscaler: check in one-shot if buffer is large enough for IP and TCP header
- MEDIUM: netscaler: do not analyze original IP packet size
- MEDIUM: netscaler: add support for standard NetScaler CIP protocol
- MINOR: spoe: add force-set-var option in spoe-agent configuration
- CONTRIB: iprange: Fix compiler warning in iprange.c
- CONTRIB: halog: Fix compiler warnings in halog.c
- BUG/MINOR: h2: properly report a stream error on RST_STREAM
- MINOR: mux: add flags to describe a mux's capabilities
- MINOR: stream-int: set flag SI_FL_CLEAN_ABRT when mux supports clean aborts
- BUG/MEDIUM: stream: don't consider abortonclose on muxes which close cleanly
- BUG/MEDIUM: checks: a server passed in maint state was not forced down.
- BUG/MEDIUM: lua: fix crash when using bogus mode in register_service()
- MINOR: http: adjust the list of supposedly cacheable methods
- MINOR: http: update the list of cacheable status codes as per RFC7231
- MINOR: http: start to compute the transaction's cacheability from the request
- BUG/MINOR: http: do not ignore cache-control: public
- BUG/MINOR: http: properly detect max-age=0 and s-maxage=0 in responses
- BUG/MINOR: cache: do not force the TX_CACHEABLE flag before checking cacheability
- MINOR: http: add a function to check request's cache-control header field
- BUG/MEDIUM: cache: do not try to retrieve host-less requests from the cache
- BUG/MEDIUM: cache: replace old object on store
- BUG/MEDIUM: cache: respect the request cache-control header
- BUG/MEDIUM: cache: don't cache the response on no-cache="set-cookie"
- BUG/MAJOR: connection: refine the situations where we don't send shutw()
- BUG/MEDIUM: checks: properly set servers to stopping state on 404
- BUG/MEDIUM: h2: properly handle and report some stream errors
- BUG/MEDIUM: h2: improve handling of frames received on closed streams
- DOC/MINOR: configuration: typo, formatting fixes
- BUG/MEDIUM: h2: ensure we always know the stream before sending a reset
- BUG/MEDIUM: mworker: don't close stdio several time
- MINOR: don't close stdio anymore
- BUG/MEDIUM: http: don't automatically forward request close
- BUG/MAJOR: hpack: don't return direct references to the dynamic headers table
- MINOR: h2: add a function to report pseudo-header names
- DEBUG: hpack: make hpack_dht_dump() expose the output file
- DEBUG: hpack: add more traces to the hpack decoder
- CONTRIB: hpack: add an hpack decoder
- MEDIUM: h2: prepare a graceful shutdown when the frontend is stopped
- BUG/MEDIUM: h2: properly handle the END_STREAM flag on empty DATA frames
- BUILD: ssl: silence a warning when building without NPN nor ALPN support
- CLEANUP: rbtree: remove
- BUG/MEDIUM: ssl: cache doesn't release shctx blocks
- BUG/MINOR: lua: Fix default value for pattern in Socket.receive
- DOC: lua: Fix typos in comments of hlua_socket_receive
- BUG/MEDIUM: lua: Fix IPv6 with separate port support for Socket.connect
- BUG/MINOR: lua: Fix return value of Socket.settimeout
- MINOR: dns: Handle SRV record weight correctly.
- BUG/MEDIUM: mworker: execvp failure depending on argv[0]
- MINOR: hathreads: add support for gcc < 4.7
- BUILD/MINOR: ancient gcc versions atomic fix
- BUG/MEDIUM: stream: properly handle client aborts during redispatch
- MINOR: spoe: add register-var-names directive in spoe-agent configuration
- MINOR: spoe: Don't queue a SPOE context if nothing is sent
- DOC: clarify the scope of ssl_fc_is_resumed
- CONTRIB: debug: fix a few flags definitions
- BUG/MINOR: poll: too large size allocation for FD events
- MINOR: sample: add date_us sample
- BUG/MEDIUM: peers: fix expire date wasn't updated if entry is modified remotely.
- MINOR: servers: Don't report duplicate dyncookies for disabled servers.
- MINOR: global/threads: move cpu_map at the end of the global struct
- MINOR: threads: add a MAX_THREADS define instead of LONGBITS
- MINOR: global: add some global activity counters to help debugging
- MINOR: threads/fd: Use a bitfield to know if there are FDs for a thread in the FD cache
- BUG/MEDIUM: threads/polling: Use fd_cache_mask instead of fd_cache_num
- BUG/MEDIUM: fd: maintain a per-thread update mask
- MINOR: fd: add a bitmask to indicate that an FD is known by the poller
- BUG/MEDIUM: epoll/threads: use one epoll_fd per thread
- BUG/MEDIUM: kqueue/threads: use one kqueue_fd per thread
- BUG/MEDIUM: threads/mworker: fix a race on startup
- BUG/MINOR: mworker: only write to pidfile if it exists
- MINOR: threads: Fix build when we're not compiling with threads.
- BUG/MINOR: threads: always set an owner to the thread_sync pipe
- BUG/MEDIUM: threads/server: Fix deadlock in srv_set_stopping/srv_set_admin_flag
- BUG/MEDIUM: checks: Don't try to release undefined conn_stream when a check is freed
- BUG/MINOR: kqueue/threads: Don't forget to close kqueue_fd[tid] on each thread
- MINOR: threads: Use __decl_hathreads instead of #ifdef/#endif
- BUILD: epoll/threads: Add test on MAX_THREADS to avoid warnings when complied without threads
- BUILD: kqueue/threads: Add test on MAX_THREADS to avoid warnings when complied without threads
- CLEANUP: sample: Fix comment encoding of sample.c
- CLEANUP: sample: Fix outdated comment about sample casts functions
- BUG/MINOR: sample: Fix output type of c_ipv62ip
- CLEANUP: Fix typo in ARGT_MSK6 comment
- CLEANUP: standard: Use len2mask4 in str2mask
- MINOR: standard: Add str2mask6 function
- MINOR: config: Add support for ARGT_MSK6
- MEDIUM: sample: Add IPv6 support to the ipmask converter
- MINOR: config: Enable tracking of up to MAX_SESS_STKCTR stick counters.
- BUG/MINOR: cli: use global.maxsock and not maxfd to list all FDs
- MINOR: polling: make epoll and kqueue not depend on maxfd anymore
- MINOR: fd: don't report maxfd in alert messages
- MEDIUM: polling: start to move maxfd computation to the pollers
- CLEANUP: fd/threads: remove the now unused fdtab_lock
- MINOR: poll: more accurately compute the new maxfd in the loop
- CLEANUP: fd: remove the unused "new" field
- MINOR: fd: move the hap_fd_{clr,set,isset} functions to fd.h
- MEDIUM: select: make use of hap_fd_* functions
- MEDIUM: fd: use atomic ops for hap_fd_{clr,set} and remove poll_lock
- MEDIUM: select: don't use the old FD state anymore
- MEDIUM: poll: don't use the old FD state anymore
- MINOR: fd: pass the iocb and owner to fd_insert()
- BUG/MINOR: threads: Update labels array because of changes in lock_label enum
- MINOR: stick-tables: Adds support for new "gpc1" and "gpc1_rate" counters.
- BUG/MINOR: epoll/threads: only call epoll_ctl(DEL) on polled FDs
- DOC: don't suggest using http-server-close
- MINOR: introduce proxy-v2-options for send-proxy-v2
- BUG/MEDIUM: spoe: Always try to receive or send the frame to detect shutdowns
- BUG/MEDIUM: spoe: Allow producer to read and to forward shutdown on request side
- MINOR: spoe: Remove check on min_applets number when a SPOE context is queued
- MINOR: spoe: Always link a SPOE context with the applet processing it
- MINOR: spoe: Replace sending_rate by a frequency counter
- MINOR: spoe: Count the number of frames waiting for an ack for each applet
- MEDIUM: spoe: Use an ebtree to manage idle applets
- MINOR: spoa_example: Count the number of frames processed by each worker
- MINOR: spoe: Add max-waiting-frames directive in spoe-agent configuration
- MINOR: init: make stdout unbuffered
- MINOR: early data: Don't rely on CO_FL_EARLY_DATA to wake up streams.
- MINOR: early data: Never remove the CO_FL_EARLY_DATA flag.
- MINOR: compiler: introduce offsetoff().
- MINOR: threads: Introduce double-width CAS on x86_64 and arm.
- MINOR: threads: add test and set/reset operations
- MINOR: pools/threads: Implement lockless memory pools.
- MAJOR: fd/threads: Make the fdcache mostly lockless.
- MEDIUM: fd/threads: Make sure we don't miss a fd cache entry.
- MAJOR: fd: compute the new fd polling state out of the fd lock
- MINOR: epoll: get rid of the now useless fd_compute_new_polled_status()
- MINOR: kqueue: get rid of the now useless fd_compute_new_polled_status()
- MINOR: poll: get rid of the now useless fd_compute_new_polled_status()
- MINOR: select: get rid of the now useless fd_compute_new_polled_status()
- CLEANUP: fd: remove the now unused fd_compute_new_polled_status() function
- MEDIUM: fd: make updt_fd_polling() use atomics
- MEDIUM: poller: use atomic ops to update the fdtab mask
- MINOR: fd: move the fd_{add_to,rm_from}_fdlist functions to fd.c
- BUG/MINOR: fd/threads: properly dereference fdcache as volatile
- MINOR: fd: remove the unneeded last CAS when adding an fd to the list
- MINOR: fd: reorder fd_add_to_fd_list()
- BUG/MINOR: time/threads: ensure the adjusted time is always correct
- BUG/MEDIUM: standard: Fix memory leak in str2ip2()
- MINOR: init: emit warning when -sf/-sd cannot parse argument
- BUILD: fd/threads: fix breakage build breakage without threads
- DOC: Describe routing impact of using interface keyword on bind lines
- DOC: Mention -Ws in the list of available options
- BUG/MINOR: config: don't emit a warning when global stats is incompletely configured
- BUG/MINOR: fd/threads: properly lock the FD before adding it to the fd cache.
- BUG/MEDIUM: threads: fix the double CAS implementation for ARMv7
- BUG/MEDIUM: ssl: Don't always treat SSL_ERROR_SYSCALL as unrecovarable.
- BUILD/MINOR: memory: stdint is needed for uintptr_t
- BUG/MINOR: init: Add missing brackets in the code parsing -sf/-st
- DOC: lua: new prototype for function "register_action()"
- DOC: cfgparse: Warn on option (tcp|http)log in backend
- BUG/MINOR: ssl/threads: Make management of the TLS ticket keys files thread-safe
- MINOR: sample: add a new "concat" converter
- BUG/MEDIUM: ssl: Shutdown the connection for reading on SSL_ERROR_SYSCALL
- BUG/MEDIUM: http: Switch the HTTP response in tunnel mode as earlier as possible
- BUG/MEDIUM: ssl/sample: ssl_bc_* fetch keywords are broken.
- MINOR: ssl/sample: adds ssl_bc_is_resumed fetch keyword.
- CLEANUP: cfgparse: Remove unused label end
- CLEANUP: spoe: Remove unused label retry
- CLEANUP: h2: Remove unused labels from mux_h2.c
- CLEANUP: pools: Remove unused end label in memory.h
- CLEANUP: standard: Fix typo in IPv6 mask example
- BUG/MINOR: pools/threads: don't ignore DEBUG_UAF on double-word CAS capable archs
- BUG/MINOR: debug/pools: properly handle out-of-memory when building with DEBUG_UAF
- MINOR: debug/pools: make DEBUG_UAF also detect underflows
- MINOR: stats: display the number of threads in the statistics.
- BUG/MINOR: h2: Set the target of dbuf_wait to h2c
- BUG/MEDIUM: h2: always consume any trailing data after end of output buffers
- BUG/MEDIUM: buffer: Fix the wrapping case in bo_putblk
- BUG/MEDIUM: buffer: Fix the wrapping case in bi_putblk
- BUG/MEDIUM: spoe: Remove idle applets from idle list when HAProxy is stopping
- Revert "BUG/MINOR: send-proxy-v2: string size must include ('\0')"
- MINOR: ssl: extract full pkey info in load_certificate
- MINOR: ssl: add ssl_sock_get_pkey_algo function
- MINOR: ssl: add ssl_sock_get_cert_sig function
- MINOR: connection: add proxy-v2-options ssl-cipher,cert-sig,cert-key
- MINOR: connection: add proxy-v2-options authority
- MINOR: systemd: Add section for SystemD sandboxing to unit file
- MINOR: systemd: Add SystemD's Protect*= options to the unit file
- MINOR: systemd: Add SystemD's SystemCallFilter option to the unit file
- CLEANUP: h2: rename misleading h2c_stream_close() to h2s_close()
- MINOR: h2: provide and use h2s_detach() and h2s_free()
- MEDIUM: h2: use a single buffer allocator
- MINOR/BUILD: fix Lua build on Mac OS X
- BUILD/MINOR: fix Lua build on Mac OS X (again)
- BUG/MINOR: session: Fix tcp-request session failure if handshake.
- CLEANUP: .gitignore: Ignore binaries from the contrib directory
- BUG/MINOR: unix: Don't mess up when removing the socket from the xfer_sock_list.
- DOC: buffers: clarify the purpose of the <from> pointer in offer_buffers()
- BUG/MEDIUM: h2: also arm the h2 timeout when sending
- BUG/MINOR: cli: Fix a crash when passing a negative or too large value to "show fd"
- CLEANUP: ssl: Remove a duplicated #include
- CLEANUP: cli: Remove a leftover debug message
- BUG/MINOR: cli: Fix a typo in the 'set rate-limit' usage
- BUG/MEDIUM: fix a 100% cpu usage with cpu-map and nbthread/nbproc
- BUG/MINOR: force-persist and ignore-persist only apply to backends
- BUG/MEDIUM: threads/unix: Fix a deadlock when a listener is temporarily disabled
- BUG/MAJOR: threads/queue: Fix thread-safety issues on the queues management
- BUG/MINOR: dns: don't downgrade DNS accepted payload size automatically
- TESTS: Add a testcase for multi-port + multi-server listener issue
- CLEANUP: dns: remove duplicate code in src/dns.c
- BUG/MINOR: seemless reload: Fix crash when an interface is specified.
- BUG/MINOR: cli: Ensure all command outputs end with a LF
- BUG/MINOR: cli: Fix a crash when sending a command with too many arguments
- BUILD: ssl: Fix build with OpenSSL without NPN capability
- BUG/MINOR: spoa-example: unexpected behavior for more than 127 args
- BUG/MINOR: lua: return bad error messages
- CLEANUP: lua/syntax: lua is a name and not an acronym
- BUG/MEDIUM: tcp-check: single connect rule can't detect DOWN servers
- BUG/MINOR: tcp-check: use the server's service port as a fallback
- BUG/MEDIUM: threads/queue: wake up other threads upon dequeue
- MINOR: log: stop emitting alerts when it's not possible to write on the socket
- BUILD/BUG: enable -fno-strict-overflow by default
- BUG/MEDIUM: fd/threads: ensure the fdcache_mask always reflects the cache contents
- DOC: log: more than 2 log servers are allowed
- MINOR: hash: add new function hash_crc32c
- MINOR: proxy-v2-options: add crc32c
- MINOR: accept-proxy: support proxy protocol v2 CRC32c checksum
- REORG: compact "struct server"
- MINOR: samples: add crc32c converter
- BUG/MEDIUM: h2: properly account for DATA padding in flow control
- BUG/MINOR: h2: ensure we can never send an RST_STREAM in response to an RST_STREAM
- BUG/MINOR: listener: Don't decrease actconn twice when a new session is rejected
- CLEANUP: map, stream: remove duplicate code in src/map.c, src/stream.c
- BUG/MINOR: lua: the function returns anything
- BUG/MINOR: lua funtion hlua_socket_settimeout don't check negative values
- CLEANUP: lua: typo fix in comments
- BUILD/MINOR: fix build when USE_THREAD is not defined
- MINOR: lua: allow socket api settimeout to accept integers, float, and doubles
- BUG/MINOR: hpack: fix harmless use of uninitialized value in hpack_dht_insert
- MINOR: cli/threads: make "show fd" report thread_sync_io_handler instead of "unknown"
- MINOR: cli: make "show fd" report the mux and mux_ctx pointers when available
- BUILD/MINOR: cli: fix a build warning introduced by last commit
- BUG/MAJOR: h2: remove orphaned streams from the send list before closing
- MINOR: h2: always call h2s_detach() in h2_detach()
- MINOR: h2: fuse h2s_detach() and h2s_free() into h2s_destroy()
- BUG/MEDIUM: h2/threads: never release the task outside of the task handler
- BUG/MEDIUM: h2: don't consider pending data on detach if connection is in error
- BUILD/MINOR: threads: always export thread_sync_io_handler()
- MINOR: mux: add a "show_fd" function to dump debugging information for "show fd"
- MINOR: h2: implement a basic "show_fd" function
- MINOR: cli: report cache indexes in "show fd"
- BUG/MINOR: h2: remove accidental debug code introduced with show_fd function
- BUG/MEDIUM: h2: always add a stream to the send or fctl list when blocked
- BUG/MINOR: checks: check the conn_stream's readiness and not the connection
- BUG/MINOR: fd: Don't clear the update_mask in fd_insert.
- BUG/MINOR: email-alert: Set the mailer port during alert initialization
- BUG/MINOR: cache: fix "show cache" output
- BUG/MAJOR: cache: fix random crashes caused by incorrect delete() on non-first blocks
- BUG/MINOR: spoe: Initialize variables used during conf parsing before any check
- BUG/MINOR: spoe: Don't release the context buffer in .check_timeouts callbaclk
- BUG/MINOR: spoe: Register the variable to set when an error occurred
- BUG/MINOR: spoe: Don't forget to decrement fpa when a processing is interrupted
- MINOR: spoe: Add metrics in to know time spent in the SPOE
- MINOR: spoe: Add options to store processing times in variables
- MINOR: log: move 'log' keyword parsing in dedicated function
- MINOR: log: Keep the ref when a log server is copied to avoid duplicate entries
- MINOR: spoe: Add loggers dedicated to the SPOE agent
- MINOR: spoe: Add support for option dontlog-normal in the SPOE agent section
- MINOR: spoe: use agent's logger to log SPOE messages
- MINOR: spoe: Add counters to log info about SPOE agents
- BUG/MAJOR: cache: always initialize newly created objects
- MINOR: servers: Support alphanumeric characters for the server templates names
- BUG/MEDIUM: threads: Fix the max/min calculation because of name clashes
- BUG/MEDIUM: connection: Make sure we have a mux before calling detach().
- BUG/MINOR: http: Return an error in proxy mode when url2sa fails
- MINOR: proxy: Add fe_defbe fetcher
- MINOR: config: Warn if resolvers has no nameservers
- BUG/MINOR: cli: Guard against NULL messages when using CLI_ST_PRINT_FREE
- MINOR: cli: Ensure the CLI always outputs an error when it should
- MEDIUM: sample: Extend functionality for field/word converters
- MINOR: export localpeer as an environment variable
- BUG/MEDIUM: kqueue: When adding new events, provide an output to get errors.
- BUILD: sample: avoid build warning in sample.c
- BUG/CRITICAL: h2: fix incorrect frame length check
- DOC: lua: update the links to the config and Lua API
- BUG/MINOR: pattern: Add a missing HA_SPIN_INIT() in pat_ref_newid()
- BUG/MAJOR: channel: Fix crash when trying to read from a closed socket
- BUG/MINOR: log: t_idle (%Ti) is not set for some requests
- BUG/MEDIUM: lua: Fix segmentation fault if a Lua task exits
- MINOR: h2: detect presence of CONNECT and/or content-length
- BUG/MEDIUM: h2: implement missing support for chunked encoded uploads
- BUG/MINOR: spoe: Fix counters update when processing is interrupted
- BUG/MINOR: spoe: Fix parsing of dontlog-normal option
- MEDIUM: cli: Add payload support
- MINOR: map: Add payload support to "add map"
- MINOR: ssl: Add payload support to "set ssl ocsp-response"
- BUG/MINOR: lua/threads: Make lua's tasks sticky to the current thread
- MINOR: sample: Add strcmp sample converter
- MINOR: http: Add support for 421 Misdirected Request
- BUG/MINOR: config: disable http-reuse on TCP proxies
- MINOR: ssl: disable SSL sample fetches when unsupported
- MINOR: ssl: add fetch 'ssl_fc_session_key' and 'ssl_bc_session_key'
- BUG/MINOR: checks: Fix check->health computation for flapping servers
- BUG/MEDIUM: threads: Fix the sync point for more than 32 threads
- BUG/MINOR, BUG/MINOR: lua: Put tasks to sleep when waiting for data
- MINOR: backend: implement random-based load balancing
- DOC/MINOR: clean up LUA documentation re: servers & array/table.
- MINOR: lua: Add server name & puid to LUA Server class.
- MINOR: lua: add get_maxconn and set_maxconn to LUA Server class.
- BUG/MINOR: map: correctly track reference to the last ref_elt being dumped
- BUG/MEDIUM: task: Don't free a task that is about to be run.
- MINOR: fd: Make the lockless fd list work with multiple lists.
- BUG/MEDIUM: pollers: Use a global list for fd shared between threads.
- MINOR: pollers: move polled_mask outside of struct fdtab.
- BUG/MINOR: lua: schedule socket task upon lua connect()
- BUG/MINOR: lua: ensure large proxy IDs can be represented
- BUG/MEDIUM: pollers/kqueue: use incremented position in event list
- BUG/MINOR: cli: don't stop cli_gen_usage_msg() when kw->usage == NULL
- BUG/MEDIUM: http: don't always abort transfers on CF_SHUTR
- BUG/MEDIUM: ssl: properly protect SSL cert generation
- BUG/MINOR: lua: Socket.send threw runtime error: 'close' needs 1 arguments.
- BUG/MINOR: spoe: Mistake in error message about SPOE configuration
- BUG/MEDIUM: spoe: Flags are not encoded in network order
- CLEANUP: spoe: Remove unused variables the agent structure
- DOC: spoe: fix a typo
- BUG/MEDIUM: contrib/mod_defender: Use network order to encode/decode flags
- BUG/MEDIUM: contrib/modsecurity: Use network order to encode/decode flags
- DOC: add some description of the pending rework of the buffer structure
- BUG/MINOR: ssl/lua: prevent lua from affecting automatic maxconn computation
- MINOR: lua: Improve error message
- BUG/MEDIUM: cache: don't cache when an Authorization header is present
- MINOR: ssl: set SSL_OP_PRIORITIZE_CHACHA
- BUG/MEDIUM: dns: Delay the attempt to run a DNS resolution on check failure.
- BUG/BUILD: threads: unbreak build without threads
- BUG/MEDIUM: servers: Add srv_addr default placeholder to the state file
- BUG/MEDIUM: lua/socket: Length required read doesn't work
- MINOR: tasks: Change the task API so that the callback takes 3 arguments.
- MAJOR: tasks: Create a per-thread runqueue.
- MAJOR: tasks: Introduce tasklets.
- MINOR: tasks: Make the number of tasks to run at once configurable.
- MAJOR: applets: Use tasks, instead of rolling our own scheduler.
- BUG/MEDIUM: stick-tables: Decrement ref_cnt in table_* converters
- MINOR: http: Log warning if (add|set)-header fails
- DOC: management: add the new wrew stats column
- MINOR: stats: also report the failed header rewrites warnings on the stats page
- BUG/MEDIUM: tasks: Don't forget to increase/decrease tasks_run_queue.
- BUG/MEDIUM: task: Don't forget to decrement max_processed after each task.
- MINOR: task: Also consider the task list size when getting global tasks.
- MINOR: dns: Implement `parse-resolv-conf` directive
- BUG/MEDIUM: spoe: Return an error when the wrong ACK is received in sync mode
- MINOR: task/notification: Is notifications registered ?
- BUG/MEDIUM: lua/socket: wrong scheduling for sockets
- BUG/MAJOR: lua: Dead lock with sockets
- BUG/MEDIUM: lua/socket: Notification error
- BUG/MEDIUM: lua/socket: Sheduling error on write: may dead-lock
- BUG/MEDIUM: lua/socket: Buffer error, may segfault
- DOC: contrib/modsecurity: few typo fixes
- DOC: SPOE.txt: fix a typo
- MAJOR: spoe: upgrade the SPOP version to 2.0 and remove the support for 1.0
- BUG/MINOR: contrib/spoa_example: Don't reset the status code during disconnect
- BUG/MINOR: contrib/mod_defender: Don't reset the status code during disconnect
- BUG/MINOR: contrib/modsecurity: Don't reset the status code during disconnect
- BUG/MINOR: contrib/mod_defender: update pointer on the end of the frame
- BUG/MINOR: contrib/modsecurity: update pointer on the end of the frame
- MINOR: task: Fix a compiler warning by adding a cast.
- MINOR: stats: also report the nice and number of calls for applets
- MINOR: applet: assign the same nice value to a new appctx as its owner task
- MINOR: task: Fix compiler warning.
- BUG/MEDIUM: tasks: Use the local runqueue when building without threads.
- MINOR: tasks: Don't define rqueue if we're building without threads.
- BUG/MINOR: unix: Make sure we can transfer abns sockets on seamless reload.
- MINOR: lua: Increase debug information
- BUG/MEDIUM: threads: handle signal queue only in thread 0
- BUG/MINOR: don't ignore SIG{BUS,FPE,ILL,SEGV} during signal processing
- BUG/MINOR: signals: ha_sigmask macro for multithreading
- BUG/MAJOR: map: fix a segfault when using http-request set-map
- DOC: regression testing: Add a short starting guide.
- MINOR: tasks: Make sure we correctly init and deinit a tasklet.
- BUG/MINOR: tasklets: Just make sure we don't pass a tasklet to the handler.
- BUG/MINOR: lua: Segfaults with wrong usage of types.
- BUG/MAJOR: ssl: Random crash with cipherlist capture
- BUG/MAJOR: ssl: OpenSSL context is stored in non-reserved memory slot
- BUG/MEDIUM: ssl: do not store pkinfo with SSL_set_ex_data
- MINOR: tests: First regression testing file.
- MINOR: reg-tests: Add reg-tests/README file.
- MINOR: reg-tests: Add a few regression testing files.
- DOC: Add new REGTEST tag info about reg testing.
- BUG/MEDIUM: fd: Don't modify the update_mask in fd_dodelete().
- MINOR: Some spelling cleanup in the comments.
- BUG/MEDIUM: threads: Use the sync point to check active jobs and exit
- MINOR: threads: Be sure to remove threads from all_threads_mask on exit
- REGTEST/MINOR: Wrong URI in a reg test for SSL/TLS.
- REGTEST/MINOR: Set HAPROXY_PROGRAM default value.
- REGTEST/MINOR: Add levels to reg-tests target.
- BUG/MAJOR: Stick-tables crash with segfault when the key is not in the stick-table
- BUG/BUILD: threads: unbreak build without threads
- BUG/MAJOR: stick_table: Complete incomplete SEGV fix
- MINOR: stick-tables: make stktable_release() do nothing on NULL
- BUG/MEDIUM: lua: possible CLOSE-WAIT state with '\n' headers
- MINOR: startup: change session/process group settings
- MINOR: systemd: consider exit status 143 as successful
- REGTEST/MINOR: Wrong URI syntax.
- CLEANUP: dns: remove obsolete macro DNS_MAX_IP_REC
- CLEANUP: dns: inacurate comment about prefered IP score
- MINOR: dns: fix wrong score computation in dns_get_ip_from_response
- MINOR: dns: new DNS options to allow/prevent IP address duplication
- REGTEST/MINOR: Unexpected curl URL globling.
- BUG/MINOR: ssl: properly ref-count the tls_keys entries
- MINOR: h2: keep a count of the number of conn_streams attached to the mux
- BUG/MEDIUM: h2: don't accept new streams if conn_streams are still in excess
- MINOR: h2: add the mux and demux buffer lengths on "show fd"
- BUG/MEDIUM: h2: never leave pending data in the output buffer on close
- BUG/MEDIUM: h2: make sure the last stream closes the connection after a timeout
- MINOR: tasklet: Set process to NULL.
- MINOR: buffer: implement a new file for low-level buffer manipulation functions
- MINOR: buffer: switch buffer sizes and offsets to size_t
- MINOR: buffer: add a few basic functions for the new API
- MINOR: buffer: Introduce b_sub(), b_add(), and bo_add()
- MINOR: buffer: Add b_set_data().
- MINOR: buffer: introduce b_realign_if_empty()
- MINOR: compression: pass the channel to http_compression_buffer_end()
- MINOR: channel: add a few basic functions for the new buffer API
- MINOR: channel/buffer: use c_realign_if_empty() instead of buffer_realign()
- MINOR: channel/buffer: replace buffer_slow_realign() with channel_slow_realign() and b_slow_realign()
- MEDIUM: channel: make channel_slow_realign() take a swap buffer
- MINOR: h2: use b_slow_realign() with the trash as a swap buffer
- MINOR: buffer: remove buffer_slow_realign() and the swap_buffer allocation code
- MINOR: channel/buffer: replace b_{adv,rew} with c_{adv,rew}
- MINOR: buffer: replace calls to buffer_space_wraps() with b_space_wraps()
- MINOR: buffer: remove bi_getblk() and bi_getblk_nc()
- MINOR: buffer: split bi_contig_data() into ci_contig_data and b_config_data()
- MINOR: buffer: remove bi_ptr()
- MINOR: buffer: remove bo_ptr()
- MINOR: buffer: remove bo_end()
- MINOR: buffer: remove bi_end()
- MINOR: buffer: remove bo_contig_data()
- MINOR: buffer: merge b{i,o}_contig_space()
- MINOR: buffer: replace bo_getblk() with direction agnostic b_getblk()
- MINOR: buffer: replace bo_getblk_nc() with b_getblk_nc() which takes an offset
- MINOR: buffer: replace bi_del() and bo_del() with b_del()
- MINOR: buffer: convert most b_ptr() calls to c_ptr()
- MINOR: h1: make h1_measure_trailers() take the byte count in argument
- MINOR: h2: clarify the fact that the send functions are unsigned
- MEDIUM: h2: prevent the various mux encoders from modifying the buffer
- MINOR: h1: make h1_skip_chunk_crlf() not depend on b_ptr() anymore
- MINOR: h1: make h1_parse_chunk_size() not depend on b_ptr() anymore
- MINOR: h1: make h1_measure_trailers() use an offset and a count
- MEDIUM: h2: do not use buf->o anymore inside h2_snd_buf's loop
- MEDIUM: h2: don't use b_ptr() nor b_end() anymore
- MINOR: buffer: get rid of b_end() and b_to_end()
- MINOR: buffer: make b_getblk_nc() take const pointers
- MINOR: buffer: make b_getblk_nc() take size_t for the block sizes
- MEDIUM: connection: make xprt->snd_buf() take the byte count in argument
- MEDIUM: mux: make mux->snd_buf() take the byte count in argument
- MEDIUM: connection: make xprt->rcv_buf() use size_t for the count
- MEDIUM: mux: make mux->rcv_buf() take a size_t for the count
- MINOR: connection: add a flags argument to rcv_buf()
- MINOR: connection: add a new receive flag : CO_RFL_BUF_WET
- MINOR: buffer: get rid of b_ptr() and convert its last users
- MINOR: buffer: use b_room() to determine available space in a buffer
- MINOR: buffer: replace buffer_not_empty() with b_data() or c_data()
- MINOR: buffer: replace buffer_empty() with b_empty() or c_empty()
- MINOR: buffer: make bo_putchar() use b_tail()
- MINOR: buffer: replace buffer_full() with channel_full()
- MINOR: buffer: replace bi_space_for_replace() with ci_space_for_replace()
- MINOR: buffer: replace buffer_pending() with ci_data()
- MINOR: buffer: replace buffer_flush() with c_adv(chn, ci_data(chn))
- MINOR: buffer: use c_head() instead of buffer_wrap_sub(c->buf, p-o)
- MINOR: buffer: use b_orig() to replace most references to b->data
- MINOR: buffer: Use b_add()/bo_add() instead of accessing b->i/b->o.
- MINOR: channel: remove almost all references to buf->i and buf->o
- MINOR: channel: Add co_set_data().
- MEDIUM: channel: adapt to the new buffer API
- MINOR: checks: adapt to the new buffer API
- MEDIUM: h2: update to the new buffer API
- MINOR: buffer: remove unused bo_add()
- MEDIUM: spoe: use the new buffer API for the SPOE buffer
- MINOR: stats: adapt to the new buffers API
- MINOR: cli: use the new buffer API
- MINOR: cache: use the new buffer API
- MINOR: stream-int: use the new buffer API
- MINOR: stream: use wrappers instead of directly manipulating buffers
- MINOR: backend: use new buffer API
- MEDIUM: http: use wrappers instead of directly manipulating buffers states
- MINOR: filters: convert to the new buffer API
- MINOR: payload: convert to the new buffer API
- MEDIUM: h1: port to new buffer API.
- MINOR: flt_trace: adapt to the new buffer API
- MEDIUM: compression: start to move to the new buffer API
- MINOR: lua: use the wrappers instead of directly manipulating buffer states
- MINOR: buffer: convert part bo_putblk() and bi_putblk() to the new API
- MINOR: buffer: adapt buffer_slow_realign() and buffer_dump() to the new API
- MAJOR: start to change buffer API
- MINOR: buffer: remove the check for output on b_del()
- MINOR: buffer: b_set_data() doesn't truncate output data anymore
- MINOR: buffer: rename the "data" field to "area"
- MEDIUM: buffers: move "output" from struct buffer to struct channel
- MINOR: buffer: replace bi_fast_delete() with b_del()
- MINOR: buffer: replace b{i,o}_put* with b_put*
- MINOR: buffer: add a new file for ist + buffer manipulation functions
- MINOR: checks: use b_putist() instead of b_putstr()
- MINOR: buffers: remove b_putstr()
- CLEANUP: buffer: minor cleanups to buffer.h
- MINOR: buffers/channel: replace buffer_insert_line2() with ci_insert_line2()
- MINOR: buffer: replace buffer_replace2() with b_rep_blk()
- MINOR: buffer: rename the data length member to '->data'
- MAJOR: buffer: finalize buffer detachment
- MEDIUM: chunks: make the chunk struct's fields match the buffer struct
- MAJOR: chunks: replace struct chunk with struct buffer
- DOC: buffers: document the new buffers API
- DOC: buffers: remove obsolete docs about buffers
- MINOR: tasklets: Don't attempt to add a tasklet in the list twice.
- MINOR: connections/mux: Add a new "subscribe" method.
- MEDIUM: connections/mux: Revamp the send direction.
- MINOR: connection: simplify subscription by adding a registration function
- BUG/MINOR: http: Set brackets for the unlikely macro at the right place
- BUG/MINOR: build: Fix compilation with debug mode enabled
- BUILD: Generate sha256 checksums in publish-release
- MINOR: debug: Add check for CO_FL_WILL_UPDATE
- MINOR: debug: Add checks for conn_stream flags
- MINOR: ist: Add the function isteqi
- BUG/MEDIUM: threads: Fix the exit condition of the thread barrier
- BUG/MEDIUM: mux_h2: Call h2_send() before updating polling.
- MINOR: buffers: simplify b_contig_space()
- MINOR: buffers: split b_putblk() into __b_putblk()
- MINOR: buffers: add b_xfer() to transfer data between buffers
- DOC: add some design notes about the new layering model
- MINOR: conn_stream: add a new CS_FL_REOS flag
- MINOR: conn_stream: add an rx buffer to the conn_stream
- MEDIUM: conn_stream: add cs_recv() as a default rcv_buf() function
- MEDIUM: stream-int: automatically call si_cs_recv_cb() if the cs has data on wake()
- MINOR: h2: make each H2 stream support an intermediary input buffer
- MEDIUM: h2: make h2_frt_decode_headers() use an intermediary buffer
- MEDIUM: h2: make h2_frt_transfer_data() copy via an intermediary buffer
- MEDIUM: h2: centralize transfer of decoded frames in h2_rcv_buf()
- MEDIUM: h2: move headers and data frame decoding to their respective parsers
- MEDIUM: buffers: make b_xfer() automatically swap buffers when possible
- MEDIUM: h2: perform a single call to the data layer in demux()
- MEDIUM: h2: don't call data_cb->recv() anymore
- MINOR: h2: make use of CS_FL_REOS to indicate that end of stream was seen
- MEDIUM: h2: use the default conn_stream's receive function
- DOC: add more design feedback on the new layering model
- MINOR: h2: add the error code and the max/last stream IDs to "show fd"
- BUG/MEDIUM: stream-int: don't immediately enable reading when the buffer was reportedly full
- BUG/MEDIUM: stats: don't ask for more data as long as we're responding
- BUG/MINOR: servers: Don't make "server" in a frontend fatal.
- BUG/MEDIUM: tasks: make sure we pick all tasks in the run queue
- BUG/MEDIUM: tasks: Decrement rqueue_size at the right time.
- BUG/MEDIUM: tasks: use atomic ops for active_tasks_mask
- BUG/MEDIUM: tasks: Make sure there's no task left before considering inactive.
- MINOR: signal: don't pass the signal number anymore as the wakeup reason
- MINOR: tasks: extend the state bits from 8 to 16 and remove the reason
- MINOR: tasks: Add a flag that tells if we're in the global runqueue.
- BUG/MEDIUM: tasks: make __task_unlink_rq responsible for the rqueue size.
- MINOR: queue: centralize dequeuing code a bit better
- MEDIUM: queue: make pendconn_free() work on the stream instead
- DOC: queue: document the expected locking model for the server's queue
- MINOR: queue: make sure pendconn->strm->pend_pos is always valid
- MINOR: queue: use a distinct variable for the assigned server and the queue
- MINOR: queue: implement pendconn queue locking functions
- MEDIUM: queue: get rid of the pendconn lock
- MINOR: tasks: Make active_tasks_mask volatile.
- MINOR: tasks: Make global_tasks_mask volatile.
- MINOR: pollers: Add a way to wake a thread sleeping in the poller.
- MINOR: threads/queue: Get rid of THREAD_WANT_SYNC in the queue code.
- BUG/MEDIUM: threads/sync: use sched_yield when available
- MINOR: ssl: BoringSSL matches OpenSSL 1.1.0
- BUG/MEDIUM: h2: prevent orphaned streams from blocking a connection forever
- BUG/MINOR: config: stick-table is not supported in defaults section
- BUILD/MINOR: threads: unbreak build with threads disabled
- BUG/MINOR: threads: Handle nbthread == MAX_THREADS.
- BUG/MEDIUM: threads: properly fix nbthreads == MAX_THREADS
- MINOR: threads: move "nbthread" parsing to hathreads.c
- BUG/MEDIUM: threads: unbreak "bind" referencing an incorrect thread number
- MEDIUM: proxy_protocol: Convert IPs to v6 when protocols are mixed
- BUILD/MINOR: compiler: fix offsetof() on older compilers
- SCRIPTS: git-show-backports: add missing quotes to "echo"
- MINOR: threads: add more consistency between certain variables in no-thread case
- MEDIUM: hathreads: implement a more flexible rendez-vous point
- BUG/MEDIUM: cli: make "show fd" thread-safe
2018-08-02 12:12:50 -04:00
printf ( " Copyright 2000-2018 Willy Tarreau <willy@haproxy.org> \n \n " ) ;
2006-06-25 20:48:02 -04:00
}
2016-12-21 12:19:57 -05:00
static void display_build_opts ( )
2007-12-02 05:28:59 -05:00
{
2016-12-21 12:43:10 -05:00
struct build_opts_str * item ;
2007-12-02 05:28:59 -05:00
printf ( " Build options : "
# ifdef BUILD_TARGET
2008-01-02 14:48:34 -05:00
" \n TARGET = " BUILD_TARGET
2007-12-02 05:28:59 -05:00
# endif
# ifdef BUILD_CPU
2008-01-02 14:48:34 -05:00
" \n CPU = " BUILD_CPU
2007-12-02 05:28:59 -05:00
# endif
# ifdef BUILD_CC
2008-01-02 14:48:34 -05:00
" \n CC = " BUILD_CC
# endif
# ifdef BUILD_CFLAGS
" \n CFLAGS = " BUILD_CFLAGS
2007-12-02 05:28:59 -05:00
# endif
2008-01-02 14:48:34 -05:00
# ifdef BUILD_OPTIONS
" \n OPTIONS = " BUILD_OPTIONS
2007-12-02 05:28:59 -05:00
# endif
2009-08-17 01:23:33 -04:00
" \n \n Default settings : "
" \n maxconn = %d, bufsize = %d, maxrewrite = %d, maxpollevents = %d "
" \n \n " ,
DEFAULT_MAXCONN , BUFSIZE , MAXREWRITE , MAX_POLL_EVENTS ) ;
2009-10-03 12:57:08 -04:00
2016-12-21 12:43:10 -05:00
list_for_each_entry ( item , & build_opts_list , list ) {
puts ( item - > str ) ;
}
2010-01-29 11:50:44 -05:00
putchar ( ' \n ' ) ;
2009-10-03 12:57:08 -04:00
list_pollers ( stdout ) ;
putchar ( ' \n ' ) ;
2018-04-10 08:37:32 -04:00
list_mux_proto ( stdout ) ;
putchar ( ' \n ' ) ;
2016-03-07 06:46:38 -05:00
list_filters ( stdout ) ;
putchar ( ' \n ' ) ;
2007-12-02 05:28:59 -05:00
}
2006-06-25 20:48:02 -04:00
/*
* This function prints the command line usage and exits
*/
2016-12-21 12:19:57 -05:00
static void usage ( char * name )
2006-06-25 20:48:02 -04:00
{
display_version ( ) ;
fprintf ( stderr ,
2016-05-13 17:52:56 -04:00
" Usage : %s [-f <cfgfile|cfgdir>]* [ -vdV "
2006-06-25 20:48:02 -04:00
" D ] [ -n <maxconn> ] [ -N <maxpconn> ] \n "
2015-10-08 05:58:48 -04:00
" [ -p <pidfile> ] [ -m <max megs> ] [ -C <dir> ] [-- <cfgfile>*] \n "
2007-12-02 05:28:59 -05:00
" -v displays version ; -vv shows known build options. \n "
2006-06-25 20:48:02 -04:00
" -d enters debug mode ; -db only disables background mode. \n "
2012-05-08 09:40:42 -04:00
" -dM[<byte>] poisons memory with <byte> (defaults to 0x50) \n "
2006-06-25 20:48:02 -04:00
" -V enters verbose mode (disables quiet mode) \n "
2011-09-10 13:26:56 -04:00
" -D goes daemon ; -C changes to <dir> before loading files. \n "
2017-06-01 11:38:50 -04:00
" -W master-worker mode. \n "
2017-11-20 09:58:35 -05:00
# if defined(USE_SYSTEMD)
" -Ws master-worker mode with systemd notify support. \n "
# endif
2006-06-25 20:48:02 -04:00
" -q quiet mode : don't display messages \n "
2009-06-22 10:02:30 -04:00
" -c check mode : only check config files and exit \n "
2006-06-25 20:48:02 -04:00
" -n sets the maximum total # of connections (%d) \n "
" -m limits the usable amount of memory (in MB) \n "
" -N sets the default, per-proxy maximum # of connections (%d) \n "
2010-09-23 12:30:22 -04:00
" -L set local peer name (default to hostname) \n "
2006-06-25 20:48:02 -04:00
" -p writes pids of all children to this file \n "
# if defined(ENABLE_EPOLL)
" -de disables epoll() usage even when available \n "
# endif
2007-04-09 06:03:06 -04:00
# if defined(ENABLE_KQUEUE)
" -dk disables kqueue() usage even when available \n "
# endif
2006-06-25 20:48:02 -04:00
# if defined(ENABLE_POLL)
" -dp disables poll() usage even when available \n "
2009-01-25 10:03:28 -05:00
# endif
2009-08-16 07:20:32 -04:00
# if defined(CONFIG_HAP_LINUX_SPLICE)
2009-01-25 10:03:28 -05:00
" -dS disables splice usage (broken on old kernels) \n "
2014-04-14 09:56:58 -04:00
# endif
# if defined(USE_GETADDRINFO)
" -dG disables getaddrinfo() usage \n "
2016-09-12 17:42:20 -04:00
# endif
# if defined(SO_REUSEPORT)
" -dR disables SO_REUSEPORT usage \n "
2006-06-25 20:48:02 -04:00
# endif
2016-11-07 15:03:16 -05:00
" -dr ignores server address resolution failures \n "
2014-01-29 06:24:34 -05:00
" -dV disables SSL verify on servers side \n "
2015-10-08 05:32:32 -04:00
" -sf/-st [pid ]* finishes/terminates old pids. \n "
2017-04-05 16:33:04 -04:00
" -x <unix_socket> get listening sockets from a unix socket \n "
2018-10-26 08:47:36 -04:00
" -S <unix_socket>[,<bind options>...] new stats socket for the master \n "
2006-06-25 20:48:02 -04:00
" \n " ,
name , DEFAULT_MAXCONN , cfg_maxpconn ) ;
exit ( 1 ) ;
}
/*********************************************************************/
/* more specific functions ***************************************/
/*********************************************************************/
2017-06-01 11:38:51 -04:00
/* sends the signal <sig> to all pids found in <oldpids>. Returns the number of
* pids the signal was correctly delivered to .
*/
static int tell_old_pids ( int sig )
{
int p ;
int ret = 0 ;
for ( p = 0 ; p < nb_oldpids ; p + + )
if ( kill ( oldpids [ p ] , sig ) = = 0 )
ret + + ;
return ret ;
}
/* return 1 if a pid is a current child otherwise 0 */
int current_child ( int pid )
{
int i ;
for ( i = 0 ; i < global . nbproc ; i + + ) {
if ( children [ i ] = = pid )
return 1 ;
}
return 0 ;
}
static void mworker_block_signals ( )
{
sigset_t set ;
sigemptyset ( & set ) ;
sigaddset ( & set , SIGUSR1 ) ;
sigaddset ( & set , SIGUSR2 ) ;
sigaddset ( & set , SIGHUP ) ;
2018-09-11 04:06:21 -04:00
sigaddset ( & set , SIGCHLD ) ;
2018-06-07 05:23:40 -04:00
ha_sigmask ( SIG_SETMASK , & set , NULL ) ;
2017-06-01 11:38:51 -04:00
}
static void mworker_unblock_signals ( )
{
2018-09-11 04:06:23 -04:00
haproxy_unblock_signals ( ) ;
2017-06-01 11:38:51 -04:00
}
/*
* Send signal to every known children .
*/
static void mworker_kill ( int sig )
{
int i ;
tell_old_pids ( sig ) ;
if ( children ) {
for ( i = 0 ; i < global . nbproc ; i + + )
kill ( children [ i ] , sig ) ;
}
}
2018-09-11 04:06:26 -04:00
/*
* serialize the proc list and put it in the environment
*/
static void mworker_proc_list_to_env ( )
{
char * msg = NULL ;
struct mworker_proc * child ;
list_for_each_entry ( child , & proc_list , list ) {
2018-11-19 12:46:18 -05:00
memprintf ( & msg , " %s|type=%c;fd=%d;pid=%d;rpid=%d;reloads=%d;timestamp=%d " , msg ? msg : " " , child - > type , child - > ipc_fd [ 0 ] , child - > pid , child - > relative_pid , child - > reloads , child - > timestamp ) ;
2018-09-11 04:06:26 -04:00
}
if ( msg )
2018-11-19 12:46:18 -05:00
setenv ( " HAPROXY_PROCESSES " , msg , 1 ) ;
2018-09-11 04:06:26 -04:00
}
/*
* unserialize the proc list from the environment
*/
static void mworker_env_to_proc_list ( )
{
char * msg , * token = NULL , * s1 ;
2018-11-19 12:46:18 -05:00
msg = getenv ( " HAPROXY_PROCESSES " ) ;
2018-09-11 04:06:26 -04:00
if ( ! msg )
return ;
while ( ( token = strtok_r ( msg , " | " , & s1 ) ) ) {
struct mworker_proc * child ;
char * subtoken = NULL ;
char * s2 ;
msg = NULL ;
child = calloc ( 1 , sizeof ( * child ) ) ;
while ( ( subtoken = strtok_r ( token , " ; " , & s2 ) ) ) {
token = NULL ;
2018-11-19 12:46:18 -05:00
if ( strncmp ( subtoken , " type= " , 5 ) = = 0 ) {
child - > type = * ( subtoken + 5 ) ;
if ( child - > type = = ' m ' ) /* we are in the master, assign it */
proc_self = child ;
} else if ( strncmp ( subtoken , " fd= " , 3 ) = = 0 ) {
2018-09-11 04:06:26 -04:00
child - > ipc_fd [ 0 ] = atoi ( subtoken + 3 ) ;
} else if ( strncmp ( subtoken , " pid= " , 4 ) = = 0 ) {
child - > pid = atoi ( subtoken + 4 ) ;
} else if ( strncmp ( subtoken , " rpid= " , 5 ) = = 0 ) {
child - > relative_pid = atoi ( subtoken + 5 ) ;
2018-10-26 08:47:29 -04:00
} else if ( strncmp ( subtoken , " reloads= " , 8 ) = = 0 ) {
/* we reloaded this process once more */
child - > reloads = atoi ( subtoken + 8 ) + 1 ;
2018-11-19 12:46:17 -05:00
} else if ( strncmp ( subtoken , " timestamp= " , 10 ) = = 0 ) {
child - > timestamp = atoi ( subtoken + 10 ) ;
2018-09-11 04:06:26 -04:00
}
}
if ( child - > pid )
LIST_ADDQ ( & proc_list , & child - > list ) ;
else
free ( child ) ;
}
2018-11-19 12:46:18 -05:00
unsetenv ( " HAPROXY_PROCESSES " ) ;
2018-09-11 04:06:26 -04:00
}
2017-11-15 13:02:58 -05:00
/*
* Upon a reload , the master worker needs to close all listeners FDs but the mworker_pipe
* fd , and the FD provided by fd @
*/
static void mworker_cleanlisteners ( )
{
struct listener * l , * l_next ;
struct proxy * curproxy ;
2017-12-05 05:14:12 -05:00
struct peers * curpeers ;
2017-11-15 13:02:58 -05:00
2018-09-11 04:06:19 -04:00
/* we might have to unbind some peers sections from some processes */
for ( curpeers = cfg_peers ; curpeers ; curpeers = curpeers - > next ) {
if ( ! curpeers - > peers_fe )
continue ;
stop_proxy ( curpeers - > peers_fe ) ;
/* disable this peer section so that it kills itself */
signal_unregister_handler ( curpeers - > sighandler ) ;
task_delete ( curpeers - > sync_task ) ;
task_free ( curpeers - > sync_task ) ;
curpeers - > sync_task = NULL ;
task_free ( curpeers - > peers_fe - > task ) ;
curpeers - > peers_fe - > task = NULL ;
curpeers - > peers_fe = NULL ;
}
2017-11-15 13:02:58 -05:00
2018-09-11 04:06:20 -04:00
for ( curproxy = proxies_list ; curproxy ; curproxy = curproxy - > next ) {
2018-12-03 14:34:44 -05:00
int listen_in_master = 0 ;
2017-11-15 13:02:58 -05:00
list_for_each_entry_safe ( l , l_next , & curproxy - > conf . listeners , by_fe ) {
2018-09-11 04:06:27 -04:00
/* remove the listener, but not those we need in the master... */
2018-10-12 04:39:54 -04:00
if ( ! ( l - > options & LI_O_MWORKER ) ) {
/* unbind the listener but does not close if
the FD is inherited with fd @ from the parent
process */
if ( l - > options & LI_O_INHERITED )
unbind_listener_no_close ( l ) ;
else
unbind_listener ( l ) ;
2018-09-11 04:06:27 -04:00
delete_listener ( l ) ;
2018-12-03 14:34:44 -05:00
} else {
listen_in_master = 1 ;
2018-10-12 04:39:54 -04:00
}
2017-11-15 13:02:58 -05:00
}
2018-12-03 14:34:44 -05:00
/* if the proxy shouldn't be in the master, we stop it */
if ( ! listen_in_master )
curproxy - > state = PR_STSTOPPED ;
2017-11-15 13:02:58 -05:00
}
}
2017-06-01 11:38:51 -04:00
/*
* remove a pid forom the olpid array and decrease nb_oldpids
* return 1 pid was found otherwise return 0
*/
int delete_oldpid ( int pid )
{
int i ;
for ( i = 0 ; i < nb_oldpids ; i + + ) {
if ( oldpids [ i ] = = pid ) {
oldpids [ i ] = oldpids [ nb_oldpids - 1 ] ;
oldpids [ nb_oldpids - 1 ] = 0 ;
nb_oldpids - - ;
return 1 ;
}
}
return 0 ;
}
2017-06-01 11:38:53 -04:00
static void get_cur_unixsocket ( )
{
/* if -x was used, try to update the stat socket if not available anymore */
if ( global . stats_fe ) {
struct bind_conf * bind_conf ;
/* pass through all stats socket */
list_for_each_entry ( bind_conf , & global . stats_fe - > conf . bind , by_fe ) {
struct listener * l ;
list_for_each_entry ( l , & bind_conf - > listeners , by_bind ) {
if ( l - > addr . ss_family = = AF_UNIX & &
( bind_conf - > level & ACCESS_FD_LISTENERS ) ) {
const struct sockaddr_un * un ;
un = ( struct sockaddr_un * ) & l - > addr ;
/* priority to old_unixsocket */
if ( ! cur_unixsocket ) {
cur_unixsocket = strdup ( un - > sun_path ) ;
} else {
if ( old_unixsocket & & ! strcmp ( un - > sun_path , old_unixsocket ) ) {
free ( cur_unixsocket ) ;
cur_unixsocket = strdup ( old_unixsocket ) ;
return ;
}
}
}
}
}
}
if ( ! cur_unixsocket & & old_unixsocket )
cur_unixsocket = strdup ( old_unixsocket ) ;
}
2017-06-01 11:38:51 -04:00
/*
* When called , this function reexec haproxy with - sf followed by current
2018-11-15 13:41:50 -05:00
* children PIDs and possibly old children PIDs if they didn ' t leave yet .
2017-06-01 11:38:51 -04:00
*/
static void mworker_reload ( )
{
int next_argc = 0 ;
int j ;
char * msg = NULL ;
2018-11-26 05:53:40 -05:00
struct per_thread_deinit_fct * ptdf ;
2017-06-01 11:38:51 -04:00
mworker_block_signals ( ) ;
2017-11-20 09:58:35 -05:00
# if defined(USE_SYSTEMD)
if ( global . tune . options & GTUNE_USE_SYSTEMD )
sd_notify ( 0 , " RELOADING=1 " ) ;
# endif
2017-06-01 11:38:51 -04:00
setenv ( " HAPROXY_MWORKER_REEXEC " , " 1 " , 1 ) ;
2018-09-11 04:06:26 -04:00
mworker_proc_list_to_env ( ) ; /* put the children description in the env */
2018-11-26 05:53:40 -05:00
/* during the reload we must ensure that every FDs that can't be
* reuse ( ie those that are not referenced in the proc_list )
* are closed or they will leak . */
/* close the listeners FD */
mworker_cli_proxy_stop ( ) ;
/* close the poller FD and the thread waker pipe FD */
list_for_each_entry ( ptdf , & per_thread_deinit_list , list )
ptdf - > fct ( ) ;
if ( fdtab )
deinit_pollers ( ) ;
2017-06-01 11:38:51 -04:00
/* compute length */
while ( next_argv [ next_argc ] )
next_argc + + ;
2017-06-01 11:38:53 -04:00
/* 1 for haproxy -sf, 2 for -x /socket */
next_argv = realloc ( next_argv , ( next_argc + 1 + 2 + global . nbproc + nb_oldpids + 1 ) * sizeof ( char * ) ) ;
2017-06-01 11:38:51 -04:00
if ( next_argv = = NULL )
goto alloc_error ;
/* add -sf <PID>* to argv */
if ( children | | nb_oldpids > 0 )
next_argv [ next_argc + + ] = " -sf " ;
if ( children ) {
for ( j = 0 ; j < global . nbproc ; next_argc + + , j + + ) {
next_argv [ next_argc ] = memprintf ( & msg , " %d " , children [ j ] ) ;
if ( next_argv [ next_argc ] = = NULL )
goto alloc_error ;
msg = NULL ;
}
}
/* copy old process PIDs */
for ( j = 0 ; j < nb_oldpids ; next_argc + + , j + + ) {
next_argv [ next_argc ] = memprintf ( & msg , " %d " , oldpids [ j ] ) ;
if ( next_argv [ next_argc ] = = NULL )
goto alloc_error ;
msg = NULL ;
}
next_argv [ next_argc ] = NULL ;
2017-06-01 11:38:53 -04:00
2017-06-20 05:20:23 -04:00
/* add the -x option with the stat socket */
2017-06-01 11:38:53 -04:00
if ( cur_unixsocket ) {
2017-06-20 05:20:23 -04:00
next_argv [ next_argc + + ] = " -x " ;
next_argv [ next_argc + + ] = ( char * ) cur_unixsocket ;
next_argv [ next_argc + + ] = NULL ;
2017-06-01 11:38:53 -04:00
}
2017-11-24 10:50:31 -05:00
ha_warning ( " Reexecuting Master process \n " ) ;
2017-11-12 11:39:18 -05:00
execvp ( next_argv [ 0 ] , next_argv ) ;
2017-06-01 11:38:51 -04:00
2017-11-24 10:50:31 -05:00
ha_warning ( " Failed to reexecute the master process [%d]: %s \n " , pid , strerror ( errno ) ) ;
2017-11-15 13:02:55 -05:00
return ;
2017-06-01 11:38:51 -04:00
alloc_error :
2018-11-15 13:43:05 -05:00
ha_warning ( " Failed to reexecute the master process [%d]: Cannot allocate memory \n " , pid ) ;
2017-06-01 11:38:51 -04:00
return ;
}
/*
2018-09-11 04:06:18 -04:00
* When called , this function reexec haproxy with - sf followed by current
2018-11-15 13:41:50 -05:00
* children PIDs and possibly old children PIDs if they didn ' t leave yet .
2017-06-01 11:38:51 -04:00
*/
2018-09-11 04:06:18 -04:00
static void mworker_catch_sighup ( struct sig_handler * sh )
2017-06-01 11:38:51 -04:00
{
2018-09-11 04:06:18 -04:00
mworker_reload ( ) ;
}
2017-06-01 11:38:51 -04:00
2018-09-11 04:06:18 -04:00
static void mworker_catch_sigterm ( struct sig_handler * sh )
{
int sig = sh - > arg ;
2017-11-15 13:02:56 -05:00
2017-11-20 09:58:35 -05:00
# if defined(USE_SYSTEMD)
2018-09-11 04:06:18 -04:00
if ( global . tune . options & GTUNE_USE_SYSTEMD ) {
sd_notify ( 0 , " STOPPING=1 " ) ;
}
2017-11-20 09:58:35 -05:00
# endif
2018-09-11 04:06:18 -04:00
ha_warning ( " Exiting Master process... \n " ) ;
mworker_kill ( sig ) ;
}
2017-11-20 09:58:35 -05:00
2018-09-11 04:06:18 -04:00
/*
* Wait for every children to exit
*/
2017-06-01 11:38:51 -04:00
2018-09-11 04:06:18 -04:00
static void mworker_catch_sigchld ( struct sig_handler * sh )
{
int exitpid = - 1 ;
int status = 0 ;
2018-09-11 04:06:26 -04:00
struct mworker_proc * child , * it ;
2017-06-01 11:38:51 -04:00
2018-09-11 04:06:18 -04:00
restart_wait :
2017-06-01 11:38:51 -04:00
2018-09-11 04:06:18 -04:00
exitpid = waitpid ( - 1 , & status , WNOHANG ) ;
if ( exitpid > 0 ) {
2017-06-01 11:38:51 -04:00
if ( WIFEXITED ( status ) )
status = WEXITSTATUS ( status ) ;
else if ( WIFSIGNALED ( status ) )
status = 128 + WTERMSIG ( status ) ;
else if ( WIFSTOPPED ( status ) )
status = 128 + WSTOPSIG ( status ) ;
else
status = 255 ;
2018-09-11 04:06:26 -04:00
list_for_each_entry_safe ( child , it , & proc_list , list ) {
if ( child - > pid ! = exitpid )
continue ;
LIST_DEL ( & child - > list ) ;
close ( child - > ipc_fd [ 0 ] ) ;
break ;
}
2017-06-01 11:38:51 -04:00
if ( ! children ) {
2018-11-06 11:37:13 -05:00
ha_warning ( " Worker %d exited with code %d (%s) \n " , exitpid , status , ( status > = 128 ) ? strsignal ( status - 128 ) : " Exit " ) ;
2017-06-01 11:38:51 -04:00
} else {
/* check if exited child was in the current children list */
if ( current_child ( exitpid ) ) {
2018-11-06 11:37:13 -05:00
ha_alert ( " Current worker #%d (%d) exited with code %d (%s) \n " , child - > relative_pid , exitpid , status , ( status > = 128 ) ? strsignal ( status - 128 ) : " Exit " ) ;
2017-06-01 11:38:54 -04:00
if ( status ! = 0 & & status ! = 130 & & status ! = 143
2017-11-24 16:02:34 -05:00
& & ! ( global . tune . options & GTUNE_NOEXIT_ONFAILURE ) ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " exit-on-failure: killing every workers with SIGTERM \n " ) ;
2018-11-06 11:37:14 -05:00
if ( exitcode < 0 )
exitcode = status ;
2017-06-01 11:38:54 -04:00
mworker_kill ( SIGTERM ) ;
}
2017-06-01 11:38:51 -04:00
} else {
2018-11-06 11:37:13 -05:00
ha_warning ( " Former worker #%d (%d) exited with code %d (%s) \n " , child - > relative_pid , exitpid , status , ( status > = 128 ) ? strsignal ( status - 128 ) : " Exit " ) ;
2017-06-01 11:38:51 -04:00
delete_oldpid ( exitpid ) ;
}
}
2018-11-06 11:37:13 -05:00
free ( child ) ;
2018-09-11 04:06:18 -04:00
/* do it again to check if it was the last worker */
goto restart_wait ;
}
/* Better rely on the system than on a list of process to check if it was the last one */
else if ( exitpid = = - 1 & & errno = = ECHILD ) {
2018-11-06 11:37:13 -05:00
ha_warning ( " All workers exited. Exiting... (%d) \n " , ( exitcode > 0 ) ? exitcode : status ) ;
2018-09-11 04:06:18 -04:00
atexit_flag = 0 ;
2018-11-06 11:37:14 -05:00
if ( exitcode > 0 )
exit ( exitcode ) ;
2018-09-11 04:06:18 -04:00
exit ( status ) ; /* parent must leave using the latest status code known */
2017-06-01 11:38:51 -04:00
}
2018-09-11 04:06:18 -04:00
2017-06-01 11:38:51 -04:00
}
2018-09-11 04:06:18 -04:00
static void mworker_loop ( )
{
# if defined(USE_SYSTEMD)
if ( global . tune . options & GTUNE_USE_SYSTEMD )
sd_notifyf ( 0 , " READY=1 \n MAINPID=%lu " , ( unsigned long ) getpid ( ) ) ;
# endif
2018-09-11 04:06:26 -04:00
master = 1 ;
2018-11-20 11:36:53 -05:00
signal_unregister ( SIGUSR1 ) ;
signal_unregister ( SIGHUP ) ;
signal_unregister ( SIGQUIT ) ;
2018-09-11 04:06:18 -04:00
signal_register_fct ( SIGTERM , mworker_catch_sigterm , SIGTERM ) ;
signal_register_fct ( SIGUSR1 , mworker_catch_sigterm , SIGUSR1 ) ;
signal_register_fct ( SIGINT , mworker_catch_sigterm , SIGINT ) ;
signal_register_fct ( SIGHUP , mworker_catch_sighup , SIGHUP ) ;
signal_register_fct ( SIGUSR2 , mworker_catch_sighup , SIGUSR2 ) ;
signal_register_fct ( SIGCHLD , mworker_catch_sigchld , SIGCHLD ) ;
mworker_unblock_signals ( ) ;
mworker_cleanlisteners ( ) ;
2018-12-06 08:05:20 -05:00
mworker_cleantasks ( ) ;
2018-09-11 04:06:18 -04:00
2018-09-11 04:06:26 -04:00
mworker_catch_sigchld ( NULL ) ; /* ensure we clean the children in case
some SIGCHLD were lost */
2018-09-11 04:06:18 -04:00
global . nbthread = 1 ;
relative_pid = 1 ;
pid_bit = 1 ;
jobs + + ; /* this is the "master" job, we want to take care of the
signals even if there is no listener so the poll loop don ' t
leave */
fork_poller ( ) ;
run_thread_poll_loop ( ( int [ ] ) { 0 } ) ;
}
2017-06-01 11:38:51 -04:00
2017-06-01 11:38:52 -04:00
/*
* Reexec the process in failure mode , instead of exiting
*/
void reexec_on_failure ( )
{
if ( ! atexit_flag )
return ;
setenv ( " HAPROXY_MWORKER_WAIT_ONLY " , " 1 " , 1 ) ;
2017-11-24 10:50:31 -05:00
ha_warning ( " Reexecuting Master process in waitpid mode \n " ) ;
2017-06-01 11:38:52 -04:00
mworker_reload ( ) ;
}
2017-06-01 11:38:51 -04:00
2006-06-25 20:48:02 -04:00
/*
2010-08-27 12:26:11 -04:00
* upon SIGUSR1 , let ' s have a soft stop . Note that soft_stop ( ) broadcasts
* a signal zero to all subscribers . This means that it ' s as easy as
* subscribing to signal 0 to get informed about an imminent shutdown .
2006-06-25 20:48:02 -04:00
*/
2016-12-21 12:19:57 -05:00
static void sig_soft_stop ( struct sig_handler * sh )
2006-06-25 20:48:02 -04:00
{
soft_stop ( ) ;
2010-08-27 11:56:48 -04:00
signal_unregister_handler ( sh ) ;
2017-11-24 11:34:44 -05:00
pool_gc ( NULL ) ;
2006-06-25 20:48:02 -04:00
}
/*
* upon SIGTTOU , we pause everything
*/
2016-12-21 12:19:57 -05:00
static void sig_pause ( struct sig_handler * sh )
2006-06-25 20:48:02 -04:00
{
pause_proxies ( ) ;
2017-11-24 11:34:44 -05:00
pool_gc ( NULL ) ;
2006-06-25 20:48:02 -04:00
}
/*
* upon SIGTTIN , let ' s have a soft stop .
*/
2016-12-21 12:19:57 -05:00
static void sig_listen ( struct sig_handler * sh )
2006-06-25 20:48:02 -04:00
{
2011-07-24 12:28:10 -04:00
resume_proxies ( ) ;
2006-06-25 20:48:02 -04:00
}
/*
* this function dumps every server ' s state when the process receives SIGHUP .
*/
2016-12-21 12:19:57 -05:00
static void sig_dump_state ( struct sig_handler * sh )
2006-06-25 20:48:02 -04:00
{
2017-11-24 10:54:05 -05:00
struct proxy * p = proxies_list ;
2006-06-25 20:48:02 -04:00
2017-11-24 10:50:31 -05:00
ha_warning ( " SIGHUP received, dumping servers states. \n " ) ;
2006-06-25 20:48:02 -04:00
while ( p ) {
struct server * s = p - > srv ;
send_log ( p , LOG_NOTICE , " SIGHUP received, dumping servers states for proxy %s. \n " , p - > id ) ;
while ( s ) {
2012-10-29 11:51:55 -04:00
chunk_printf ( & trash ,
" SIGHUP: Server %s/%s is %s. Conn: %d act, %d pend, %lld tot. " ,
p - > id , s - > id ,
2017-08-31 08:41:55 -04:00
( s - > cur_state ! = SRV_ST_STOPPED ) ? " UP " : " DOWN " ,
2012-10-29 11:51:55 -04:00
s - > cur_sess , s - > nbpend , s - > counters . cum_sess ) ;
2018-07-13 04:54:26 -04:00
ha_warning ( " %s \n " , trash . area ) ;
send_log ( p , LOG_NOTICE , " %s \n " , trash . area ) ;
2006-06-25 20:48:02 -04:00
s = s - > next ;
}
2007-09-17 05:27:09 -04:00
/* FIXME: those info are a bit outdated. We should be able to distinguish between FE and BE. */
if ( ! p - > srv ) {
2012-10-29 11:51:55 -04:00
chunk_printf ( & trash ,
" SIGHUP: Proxy %s has no servers. Conn: act(FE+BE): %d+%d, %d pend (%d unass), tot(FE+BE): %lld+%lld. " ,
p - > id ,
p - > feconn , p - > beconn , p - > totpend , p - > nbpend , p - > fe_counters . cum_conn , p - > be_counters . cum_conn ) ;
2007-09-17 05:27:09 -04:00
} else if ( p - > srv_act = = 0 ) {
2012-10-29 11:51:55 -04:00
chunk_printf ( & trash ,
" SIGHUP: Proxy %s %s ! Conn: act(FE+BE): %d+%d, %d pend (%d unass), tot(FE+BE): %lld+%lld. " ,
p - > id ,
( p - > srv_bck ) ? " is running on backup servers " : " has no server available " ,
p - > feconn , p - > beconn , p - > totpend , p - > nbpend , p - > fe_counters . cum_conn , p - > be_counters . cum_conn ) ;
2006-06-25 20:48:02 -04:00
} else {
2012-10-29 11:51:55 -04:00
chunk_printf ( & trash ,
" SIGHUP: Proxy %s has %d active servers and %d backup servers available. "
" Conn: act(FE+BE): %d+%d, %d pend (%d unass), tot(FE+BE): %lld+%lld. " ,
p - > id , p - > srv_act , p - > srv_bck ,
p - > feconn , p - > beconn , p - > totpend , p - > nbpend , p - > fe_counters . cum_conn , p - > be_counters . cum_conn ) ;
2006-06-25 20:48:02 -04:00
}
2018-07-13 04:54:26 -04:00
ha_warning ( " %s \n " , trash . area ) ;
send_log ( p , LOG_NOTICE , " %s \n " , trash . area ) ;
2006-06-25 20:48:02 -04:00
p = p - > next ;
}
}
2016-12-21 12:19:57 -05:00
static void dump ( struct sig_handler * sh )
2006-06-25 20:48:02 -04:00
{
2007-05-13 13:43:47 -04:00
/* dump memory usage then free everything possible */
dump_pools ( ) ;
2017-11-24 11:34:44 -05:00
pool_gc ( NULL ) ;
2006-06-25 20:48:02 -04:00
}
2017-12-28 10:09:36 -05:00
/*
* This function dup2 the stdio FDs ( 0 , 1 , 2 ) with < fd > , then closes < fd >
* If < fd > < 0 , it opens / dev / null and use it to dup
*
* In the case of chrooting , you have to open / dev / null before the chroot , and
* pass the < fd > to this function
*/
static void stdio_quiet ( int fd )
{
if ( fd < 0 )
fd = open ( " /dev/null " , O_RDWR , 0 ) ;
if ( fd > - 1 ) {
fclose ( stdin ) ;
fclose ( stdout ) ;
fclose ( stderr ) ;
dup2 ( fd , 0 ) ;
dup2 ( fd , 1 ) ;
dup2 ( fd , 2 ) ;
if ( fd > 2 )
close ( fd ) ;
return ;
}
ha_alert ( " Cannot open /dev/null \n " ) ;
exit ( EXIT_FAILURE ) ;
}
2018-11-15 13:41:50 -05:00
/* This function checks if cfg_cfgfiles contains directories.
* If it finds one , it adds all the files ( and only files ) it contains
* in cfg_cfgfiles in place of the directory ( and removes the directory ) .
* It adds the files in lexical order .
* It adds only files with . cfg extension .
2016-05-13 17:52:56 -04:00
* It doesn ' t add files with name starting with ' . '
*/
2016-12-21 12:19:57 -05:00
static void cfgfiles_expand_directories ( void )
2016-05-13 17:52:56 -04:00
{
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 = NULL ;
int dir_entries_nb ;
int dir_entries_it ;
if ( stat ( wl - > s , & file_stat ) ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " Cannot open configuration file/directory %s : %s \n " ,
wl - > s ,
strerror ( errno ) ) ;
2016-05-13 17:52:56 -04:00
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 ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " Cannot open configuration directory %s : %s \n " ,
wl - > s ,
strerror ( errno ) ) ;
2016-05-13 17:52:56 -04:00
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 ;
char * d_name_cfgext = strstr ( dir_entry - > d_name , " .cfg " ) ;
/* don't add filename that begin with .
2018-11-15 13:41:50 -05:00
* only add filename with . cfg extension
2016-05-13 17:52:56 -04:00
*/
if ( dir_entry - > d_name [ 0 ] = = ' . ' | |
! ( d_name_cfgext & & d_name_cfgext [ 4 ] = = ' \0 ' ) )
goto next_dir_entry ;
if ( ! memprintf ( & filename , " %s/%s " , wl - > s , dir_entry - > d_name ) ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " Cannot load configuration files %s : out of memory. \n " ,
filename ) ;
2016-05-13 17:52:56 -04:00
exit ( 1 ) ;
}
if ( stat ( filename , & file_stat ) ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " Cannot open configuration file %s : %s \n " ,
wl - > s ,
strerror ( errno ) ) ;
2016-05-13 17:52:56 -04:00
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 ) )
goto next_dir_entry ;
if ( ! list_append_word ( & wl - > list , filename , & err ) ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " Cannot load configuration files %s : %s \n " ,
filename ,
err ) ;
2016-05-13 17:52:56 -04:00
exit ( 1 ) ;
}
next_dir_entry :
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 ) ;
}
2017-04-05 16:33:04 -04:00
static int get_old_sockets ( const char * unixsocket )
{
char * cmsgbuf = NULL , * tmpbuf = NULL ;
int * tmpfd = NULL ;
struct sockaddr_un addr ;
struct cmsghdr * cmsg ;
struct msghdr msghdr ;
struct iovec iov ;
struct xfer_sock_list * xfer_sock = NULL ;
2017-04-06 08:45:14 -04:00
struct timeval tv = { . tv_sec = 1 , . tv_usec = 0 } ;
2017-04-05 16:33:04 -04:00
int sock = - 1 ;
int ret = - 1 ;
int ret2 = - 1 ;
int fd_nb ;
int got_fd = 0 ;
int i = 0 ;
size_t maxoff = 0 , curoff = 0 ;
memset ( & msghdr , 0 , sizeof ( msghdr ) ) ;
cmsgbuf = malloc ( CMSG_SPACE ( sizeof ( int ) ) * MAX_SEND_FD ) ;
if ( ! cmsgbuf ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Failed to allocate memory to send sockets \n " ) ;
2017-04-05 16:33:04 -04:00
goto out ;
}
sock = socket ( PF_UNIX , SOCK_STREAM , 0 ) ;
if ( sock < 0 ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Failed to connect to the old process socket '%s' \n " ,
unixsocket ) ;
2017-04-05 16:33:04 -04:00
goto out ;
}
strncpy ( addr . sun_path , unixsocket , sizeof ( addr . sun_path ) ) ;
addr . sun_path [ sizeof ( addr . sun_path ) - 1 ] = 0 ;
addr . sun_family = PF_UNIX ;
ret = connect ( sock , ( struct sockaddr * ) & addr , sizeof ( addr ) ) ;
if ( ret < 0 ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Failed to connect to the old process socket '%s' \n " ,
unixsocket ) ;
2017-04-05 16:33:04 -04:00
goto out ;
}
2017-04-06 08:45:14 -04:00
setsockopt ( sock , SOL_SOCKET , SO_RCVTIMEO , ( void * ) & tv , sizeof ( tv ) ) ;
2017-04-05 16:33:04 -04:00
iov . iov_base = & fd_nb ;
iov . iov_len = sizeof ( fd_nb ) ;
msghdr . msg_iov = & iov ;
msghdr . msg_iovlen = 1 ;
send ( sock , " _getsocks \n " , strlen ( " _getsocks \n " ) , 0 ) ;
/* First, get the number of file descriptors to be received */
if ( recvmsg ( sock , & msghdr , MSG_WAITALL ) ! = sizeof ( fd_nb ) ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Failed to get the number of sockets to be transferred ! \n " ) ;
2017-04-05 16:33:04 -04:00
goto out ;
}
if ( fd_nb = = 0 ) {
ret = 0 ;
goto out ;
}
2017-11-04 10:13:01 -04:00
tmpbuf = malloc ( fd_nb * ( 1 + MAXPATHLEN + 1 + IFNAMSIZ + sizeof ( int ) ) ) ;
2017-04-05 16:33:04 -04:00
if ( tmpbuf = = NULL ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Failed to allocate memory while receiving sockets \n " ) ;
2017-04-05 16:33:04 -04:00
goto out ;
}
tmpfd = malloc ( fd_nb * sizeof ( int ) ) ;
if ( tmpfd = = NULL ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Failed to allocate memory while receiving sockets \n " ) ;
2017-04-05 16:33:04 -04:00
goto out ;
}
msghdr . msg_control = cmsgbuf ;
msghdr . msg_controllen = CMSG_SPACE ( sizeof ( int ) ) * MAX_SEND_FD ;
2017-11-04 10:13:01 -04:00
iov . iov_len = MAX_SEND_FD * ( 1 + MAXPATHLEN + 1 + IFNAMSIZ + sizeof ( int ) ) ;
2017-04-05 16:33:04 -04:00
do {
int ret3 ;
iov . iov_base = tmpbuf + curoff ;
ret = recvmsg ( sock , & msghdr , 0 ) ;
if ( ret = = - 1 & & errno = = EINTR )
continue ;
if ( ret < = 0 )
break ;
/* Send an ack to let the sender know we got the sockets
* and it can send some more
*/
do {
ret3 = send ( sock , & got_fd , sizeof ( got_fd ) , 0 ) ;
} while ( ret3 = = - 1 & & errno = = EINTR ) ;
for ( cmsg = CMSG_FIRSTHDR ( & msghdr ) ; cmsg ! = NULL ;
cmsg = CMSG_NXTHDR ( & msghdr , cmsg ) ) {
if ( cmsg - > cmsg_level = = SOL_SOCKET & &
cmsg - > cmsg_type = = SCM_RIGHTS ) {
size_t totlen = cmsg - > cmsg_len -
CMSG_LEN ( 0 ) ;
if ( totlen / sizeof ( int ) + got_fd > fd_nb ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Got to many sockets ! \n " ) ;
2017-04-05 16:33:04 -04:00
goto out ;
}
/*
* Be paranoid and use memcpy ( ) to avoid any
* potential alignement issue .
*/
memcpy ( & tmpfd [ got_fd ] , CMSG_DATA ( cmsg ) , totlen ) ;
got_fd + = totlen / sizeof ( int ) ;
}
}
curoff + = ret ;
} while ( got_fd < fd_nb ) ;
if ( got_fd ! = fd_nb ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " We didn't get the expected number of sockets (expecting %d got %d) \n " ,
fd_nb , got_fd ) ;
2017-04-05 16:33:04 -04:00
goto out ;
}
maxoff = curoff ;
curoff = 0 ;
for ( i = 0 ; i < got_fd ; i + + ) {
int fd = tmpfd [ i ] ;
socklen_t socklen ;
int len ;
xfer_sock = calloc ( 1 , sizeof ( * xfer_sock ) ) ;
if ( ! xfer_sock ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Failed to allocate memory in get_old_sockets() ! \n " ) ;
2017-04-05 16:33:04 -04:00
break ;
}
xfer_sock - > fd = - 1 ;
socklen = sizeof ( xfer_sock - > addr ) ;
if ( getsockname ( fd , ( struct sockaddr * ) & xfer_sock - > addr , & socklen ) ! = 0 ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Failed to get socket address \n " ) ;
2017-04-05 16:33:04 -04:00
free ( xfer_sock ) ;
2017-07-17 11:25:33 -04:00
xfer_sock = NULL ;
2017-04-05 16:33:04 -04:00
continue ;
}
if ( curoff > = maxoff ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Inconsistency while transferring sockets \n " ) ;
2017-04-05 16:33:04 -04:00
goto out ;
}
len = tmpbuf [ curoff + + ] ;
if ( len > 0 ) {
/* We have a namespace */
if ( curoff + len > maxoff ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Inconsistency while transferring sockets \n " ) ;
2017-04-05 16:33:04 -04:00
goto out ;
}
xfer_sock - > namespace = malloc ( len + 1 ) ;
if ( ! xfer_sock - > namespace ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Failed to allocate memory while transferring sockets \n " ) ;
2017-04-05 16:33:04 -04:00
goto out ;
}
memcpy ( xfer_sock - > namespace , & tmpbuf [ curoff ] , len ) ;
xfer_sock - > namespace [ len ] = 0 ;
curoff + = len ;
}
if ( curoff > = maxoff ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Inconsistency while transferring sockets \n " ) ;
2017-04-05 16:33:04 -04:00
goto out ;
}
len = tmpbuf [ curoff + + ] ;
if ( len > 0 ) {
/* We have an interface */
if ( curoff + len > maxoff ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Inconsistency while transferring sockets \n " ) ;
2017-04-05 16:33:04 -04:00
goto out ;
}
xfer_sock - > iface = malloc ( len + 1 ) ;
if ( ! xfer_sock - > iface ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Failed to allocate memory while transferring sockets \n " ) ;
2017-04-05 16:33:04 -04:00
goto out ;
}
memcpy ( xfer_sock - > iface , & tmpbuf [ curoff ] , len ) ;
2018-03-15 12:48:49 -04:00
xfer_sock - > iface [ len ] = 0 ;
2017-04-05 16:33:04 -04:00
curoff + = len ;
}
if ( curoff + sizeof ( int ) > maxoff ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Inconsistency while transferring sockets \n " ) ;
2017-04-05 16:33:04 -04:00
goto out ;
}
memcpy ( & xfer_sock - > options , & tmpbuf [ curoff ] ,
sizeof ( xfer_sock - > options ) ) ;
curoff + = sizeof ( xfer_sock - > options ) ;
xfer_sock - > fd = fd ;
if ( xfer_sock_list )
xfer_sock_list - > prev = xfer_sock ;
xfer_sock - > next = xfer_sock_list ;
xfer_sock - > prev = NULL ;
xfer_sock_list = xfer_sock ;
xfer_sock = NULL ;
}
ret2 = 0 ;
out :
/* If we failed midway make sure to close the remaining
* file descriptors
*/
if ( tmpfd ! = NULL & & i < got_fd ) {
for ( ; i < got_fd ; i + + ) {
close ( tmpfd [ i ] ) ;
}
}
free ( tmpbuf ) ;
free ( tmpfd ) ;
free ( cmsgbuf ) ;
if ( sock ! = - 1 )
close ( sock ) ;
if ( xfer_sock ) {
free ( xfer_sock - > namespace ) ;
free ( xfer_sock - > iface ) ;
if ( xfer_sock - > fd ! = - 1 )
close ( xfer_sock - > fd ) ;
free ( xfer_sock ) ;
}
return ( ret2 ) ;
}
2017-06-01 11:38:51 -04:00
/*
* copy and cleanup the current argv
* Remove the - sf / - st parameters
* Return an allocated copy of argv
*/
static char * * copy_argv ( int argc , char * * argv )
{
char * * newargv ;
2017-06-20 05:20:23 -04:00
int i = 0 , j = 0 ;
2017-06-01 11:38:51 -04:00
newargv = calloc ( argc + 2 , sizeof ( char * ) ) ;
if ( newargv = = NULL ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Cannot allocate memory \n " ) ;
2017-06-01 11:38:51 -04:00
return NULL ;
}
2017-06-20 05:20:23 -04:00
while ( i < argc ) {
/* -sf or -st or -x */
2018-01-09 17:12:27 -05:00
if ( i > 0 & & argv [ i ] [ 0 ] = = ' - ' & &
( ( argv [ i ] [ 1 ] = = ' s ' & & ( argv [ i ] [ 2 ] = = ' f ' | | argv [ i ] [ 2 ] = = ' t ' ) ) | | argv [ i ] [ 1 ] = = ' x ' ) ) {
2017-06-20 05:20:23 -04:00
/* list of pids to finish ('f') or terminate ('t') or unix socket (-x) */
2017-06-01 11:38:51 -04:00
i + + ;
while ( i < argc & & argv [ i ] [ 0 ] ! = ' - ' ) {
i + + ;
}
2017-06-20 05:20:23 -04:00
continue ;
2017-06-01 11:38:51 -04:00
}
2017-06-20 05:20:23 -04:00
newargv [ j + + ] = argv [ i + + ] ;
2017-06-01 11:38:51 -04:00
}
2017-06-20 05:20:23 -04:00
2017-06-01 11:38:51 -04:00
return newargv ;
}
2006-06-25 20:48:02 -04:00
/*
* This function initializes all the necessary variables . It only returns
* if everything is OK . If something fails , it exits .
*/
2016-12-21 12:19:57 -05:00
static void init ( int argc , char * * argv )
2006-06-25 20:48:02 -04:00
{
int arg_mode = 0 ; /* MODE_DEBUG, ... */
char * tmp ;
char * cfg_pidfile = NULL ;
2009-07-20 03:30:05 -04:00
int err_code = 0 ;
2016-05-13 17:52:55 -04:00
char * err_msg = NULL ;
2010-01-03 15:12:30 -05:00
struct wordlist * wl ;
2010-12-22 11:08:21 -05:00
char * progname ;
2011-09-10 13:26:56 -04:00
char * change_dir = NULL ;
MAJOR: filters: Add filters support
This patch adds the support of filters in HAProxy. The main idea is to have a
way to "easely" extend HAProxy by adding some "modules", called filters, that
will be able to change HAProxy behavior in a programmatic way.
To do so, many entry points has been added in code to let filters to hook up to
different steps of the processing. A filter must define a flt_ops sutrctures
(see include/types/filters.h for details). This structure contains all available
callbacks that a filter can define:
struct flt_ops {
/*
* Callbacks to manage the filter lifecycle
*/
int (*init) (struct proxy *p);
void (*deinit)(struct proxy *p);
int (*check) (struct proxy *p);
/*
* Stream callbacks
*/
void (*stream_start) (struct stream *s);
void (*stream_accept) (struct stream *s);
void (*session_establish)(struct stream *s);
void (*stream_stop) (struct stream *s);
/*
* HTTP callbacks
*/
int (*http_start) (struct stream *s, struct http_msg *msg);
int (*http_start_body) (struct stream *s, struct http_msg *msg);
int (*http_start_chunk) (struct stream *s, struct http_msg *msg);
int (*http_data) (struct stream *s, struct http_msg *msg);
int (*http_last_chunk) (struct stream *s, struct http_msg *msg);
int (*http_end_chunk) (struct stream *s, struct http_msg *msg);
int (*http_chunk_trailers)(struct stream *s, struct http_msg *msg);
int (*http_end_body) (struct stream *s, struct http_msg *msg);
void (*http_end) (struct stream *s, struct http_msg *msg);
void (*http_reset) (struct stream *s, struct http_msg *msg);
int (*http_pre_process) (struct stream *s, struct http_msg *msg);
int (*http_post_process) (struct stream *s, struct http_msg *msg);
void (*http_reply) (struct stream *s, short status,
const struct chunk *msg);
};
To declare and use a filter, in the configuration, the "filter" keyword must be
used in a listener/frontend section:
frontend test
...
filter <FILTER-NAME> [OPTIONS...]
The filter referenced by the <FILTER-NAME> must declare a configuration parser
on its own name to fill flt_ops and filter_conf field in the proxy's
structure. An exemple will be provided later to make it perfectly clear.
For now, filters cannot be used in backend section. But this is only a matter of
time. Documentation will also be added later. This is the first commit of a long
list about filters.
It is possible to have several filters on the same listener/frontend. These
filters are stored in an array of at most MAX_FILTERS elements (define in
include/types/filters.h). Again, this will be replaced later by a list of
filters.
The filter API has been highly refactored. Main changes are:
* Now, HA supports an infinite number of filters per proxy. To do so, filters
are stored in list.
* Because filters are stored in list, filters state has been moved from the
channel structure to the filter structure. This is cleaner because there is no
more info about filters in channel structure.
* It is possible to defined filters on backends only. For such filters,
stream_start/stream_stop callbacks are not called. Of course, it is possible
to mix frontend and backend filters.
* Now, TCP streams are also filtered. All callbacks without the 'http_' prefix
are called for all kind of streams. In addition, 2 new callbacks were added to
filter data exchanged through a TCP stream:
- tcp_data: it is called when new data are available or when old unprocessed
data are still waiting.
- tcp_forward_data: it is called when some data can be consumed.
* New callbacks attached to channel were added:
- channel_start_analyze: it is called when a filter is ready to process data
exchanged through a channel. 2 new analyzers (a frontend and a backend)
are attached to channels to call this callback. For a frontend filter, it
is called before any other analyzer. For a backend filter, it is called
when a backend is attached to a stream. So some processing cannot be
filtered in that case.
- channel_analyze: it is called before each analyzer attached to a channel,
expects analyzers responsible for data sending.
- channel_end_analyze: it is called when all other analyzers have finished
their processing. A new analyzers is attached to channels to call this
callback. For a TCP stream, this is always the last one called. For a HTTP
one, the callback is called when a request/response ends, so it is called
one time for each request/response.
* 'session_established' callback has been removed. Everything that is done in
this callback can be handled by 'channel_start_analyze' on the response
channel.
* 'http_pre_process' and 'http_post_process' callbacks have been replaced by
'channel_analyze'.
* 'http_start' callback has been replaced by 'http_headers'. This new one is
called just before headers sending and parsing of the body.
* 'http_end' callback has been replaced by 'channel_end_analyze'.
* It is possible to set a forwarder for TCP channels. It was already possible to
do it for HTTP ones.
* Forwarders can partially consumed forwardable data. For this reason a new
HTTP message state was added before HTTP_MSG_DONE : HTTP_MSG_ENDING.
Now all filters can define corresponding callbacks (http_forward_data
and tcp_forward_data). Each filter owns 2 offsets relative to buf->p, next and
forward, to track, respectively, input data already parsed but not forwarded yet
by the filter and parsed data considered as forwarded by the filter. A any time,
we have the warranty that a filter cannot parse or forward more input than
previous ones. And, of course, it cannot forward more input than it has
parsed. 2 macros has been added to retrieve these offets: FLT_NXT and FLT_FWD.
In addition, 2 functions has been added to change the 'next size' and the
'forward size' of a filter. When a filter parses input data, it can alter these
data, so the size of these data can vary. This action has an effet on all
previous filters that must be handled. To do so, the function
'filter_change_next_size' must be called, passing the size variation. In the
same spirit, if a filter alter forwarded data, it must call the function
'filter_change_forward_size'. 'filter_change_next_size' can be called in
'http_data' and 'tcp_data' callbacks and only these ones. And
'filter_change_forward_size' can be called in 'http_forward_data' and
'tcp_forward_data' callbacks and only these ones. The data changes are the
filter responsability, but with some limitation. It must not change already
parsed/forwarded data or data that previous filters have not parsed/forwarded
yet.
Because filters can be used on backends, when we the backend is set for a
stream, we add filters defined for this backend in the filter list of the
stream. But we must only do that when the backend and the frontend of the stream
are not the same. Else same filters are added a second time leading to undefined
behavior.
The HTTP compression code had to be moved.
So it simplifies http_response_forward_body function. To do so, the way the data
are forwarded has changed. Now, a filter (and only one) can forward data. In a
commit to come, this limitation will be removed to let all filters take part to
data forwarding. There are 2 new functions that filters should use to deal with
this feature:
* flt_set_http_data_forwarder: This function sets the filter (using its id)
that will forward data for the specified HTTP message. It is possible if it
was not already set by another filter _AND_ if no data was yet forwarded
(msg->msg_state <= HTTP_MSG_BODY). It returns -1 if an error occurs.
* flt_http_data_forwarder: This function returns the filter id that will
forward data for the specified HTTP message. If there is no forwarder set, it
returns -1.
When an HTTP data forwarder is set for the response, the HTTP compression is
disabled. Of course, this is not definitive.
2015-04-30 05:48:27 -04:00
struct proxy * px ;
2016-12-21 13:57:00 -05:00
struct post_check_fct * pcf ;
2006-06-25 20:48:02 -04:00
2017-10-24 07:53:54 -04:00
global . mode = MODE_STARTING ;
2017-06-01 11:38:51 -04:00
next_argv = copy_argv ( argc , argv ) ;
2017-10-27 07:53:47 -04:00
if ( ! init_trash_buffers ( 1 ) ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " failed to initialize trash buffers. \n " ) ;
2017-07-26 08:59:46 -04:00
exit ( 1 ) ;
}
2012-05-16 08:16:48 -04:00
2010-09-23 12:30:22 -04:00
/* NB: POSIX does not make it mandatory for gethostname() to NULL-terminate
* the string in case of truncation , and at least FreeBSD appears not to do
* it .
*/
memset ( hostname , 0 , sizeof ( hostname ) ) ;
gethostname ( hostname , sizeof ( hostname ) - 1 ) ;
memset ( localpeer , 0 , sizeof ( localpeer ) ) ;
memcpy ( localpeer , hostname , ( sizeof ( hostname ) > sizeof ( localpeer ) ? sizeof ( localpeer ) : sizeof ( hostname ) ) - 1 ) ;
2018-04-17 10:46:13 -04:00
setenv ( " HAPROXY_LOCALPEER " , localpeer , 1 ) ;
2010-09-23 12:30:22 -04:00
2006-06-25 20:48:02 -04:00
/*
* Initialize the previously static variables .
*/
2017-10-27 07:53:47 -04:00
2018-01-26 15:48:23 -05:00
totalconn = actconn = listeners = stopping = 0 ;
2017-03-23 17:44:13 -04:00
killed = 0 ;
2017-10-27 07:53:47 -04:00
2006-06-25 20:48:02 -04:00
# ifdef HAPROXY_MEMMAX
2015-12-14 06:46:07 -05:00
global . rlimit_memmax_all = HAPROXY_MEMMAX ;
2006-06-25 20:48:02 -04:00
# endif
2016-03-27 05:08:03 -04:00
tzset ( ) ;
2008-06-23 08:00:57 -04:00
tv_update_date ( - 1 , - 1 ) ;
2006-06-25 20:48:02 -04:00
start_date = now ;
2014-02-14 05:59:04 -05:00
srandom ( now_ms - getpid ( ) ) ;
2013-01-11 09:49:37 -05:00
if ( init_acl ( ) ! = 0 )
exit ( 1 ) ;
2018-11-26 10:31:20 -05:00
2009-09-23 17:37:52 -04:00
/* warning, we init buffers later */
2018-09-10 12:04:24 -04:00
if ( ! init_http ( & err_msg ) ) {
ha_alert ( " %s. Aborting. \n " , err_msg ) ;
free ( err_msg ) ;
abort ( ) ;
}
2006-06-25 20:48:02 -04:00
2015-01-23 08:06:13 -05:00
/* Initialise lua. */
hlua_init ( ) ;
2016-11-09 05:36:17 -05:00
/* Initialize process vars */
vars_init ( & global . vars , SCOPE_PROC ) ;
2009-01-25 09:42:27 -05:00
global . tune . options | = GTUNE_USE_SELECT ; /* select() is always available */
2006-06-25 20:48:02 -04:00
# if defined(ENABLE_POLL)
2009-01-25 09:42:27 -05:00
global . tune . options | = GTUNE_USE_POLL ;
2006-06-25 20:48:02 -04:00
# endif
# if defined(ENABLE_EPOLL)
2009-01-25 09:42:27 -05:00
global . tune . options | = GTUNE_USE_EPOLL ;
2006-06-25 20:48:02 -04:00
# endif
2007-04-09 06:03:06 -04:00
# if defined(ENABLE_KQUEUE)
2009-01-25 09:42:27 -05:00
global . tune . options | = GTUNE_USE_KQUEUE ;
2007-04-09 06:03:06 -04:00
# endif
2009-08-16 07:20:32 -04:00
# if defined(CONFIG_HAP_LINUX_SPLICE)
2009-01-25 10:03:28 -05:00
global . tune . options | = GTUNE_USE_SPLICE ;
# endif
2014-04-14 09:56:58 -04:00
# if defined(USE_GETADDRINFO)
global . tune . options | = GTUNE_USE_GAI ;
# endif
2016-09-12 17:42:20 -04:00
# if defined(SO_REUSEPORT)
global . tune . options | = GTUNE_USE_REUSEPORT ;
# endif
2006-06-25 20:48:02 -04:00
pid = getpid ( ) ;
progname = * argv ;
while ( ( tmp = strchr ( progname , ' / ' ) ) ! = NULL )
progname = tmp + 1 ;
2010-12-22 11:08:21 -05:00
/* the process name is used for the logs only */
2015-10-01 07:18:13 -04:00
chunk_initstr ( & global . log_tag , strdup ( progname ) ) ;
2010-12-22 11:08:21 -05:00
2006-06-25 20:48:02 -04:00
argc - - ; argv + + ;
while ( argc > 0 ) {
char * flag ;
if ( * * argv = = ' - ' ) {
flag = * argv + 1 ;
/* 1 arg */
if ( * flag = = ' v ' ) {
display_version ( ) ;
2007-12-02 05:28:59 -05:00
if ( flag [ 1 ] = = ' v ' ) /* -vv */
display_build_opts ( ) ;
2006-06-25 20:48:02 -04:00
exit ( 0 ) ;
}
# if defined(ENABLE_EPOLL)
else if ( * flag = = ' d ' & & flag [ 1 ] = = ' e ' )
2009-01-25 09:42:27 -05:00
global . tune . options & = ~ GTUNE_USE_EPOLL ;
2006-06-25 20:48:02 -04:00
# endif
# if defined(ENABLE_POLL)
else if ( * flag = = ' d ' & & flag [ 1 ] = = ' p ' )
2009-01-25 09:42:27 -05:00
global . tune . options & = ~ GTUNE_USE_POLL ;
2007-04-09 06:03:06 -04:00
# endif
2007-04-10 16:45:11 -04:00
# if defined(ENABLE_KQUEUE)
2007-04-09 06:03:06 -04:00
else if ( * flag = = ' d ' & & flag [ 1 ] = = ' k ' )
2009-01-25 09:42:27 -05:00
global . tune . options & = ~ GTUNE_USE_KQUEUE ;
2009-01-25 10:03:28 -05:00
# endif
2009-08-16 07:20:32 -04:00
# if defined(CONFIG_HAP_LINUX_SPLICE)
2009-01-25 10:03:28 -05:00
else if ( * flag = = ' d ' & & flag [ 1 ] = = ' S ' )
global . tune . options & = ~ GTUNE_USE_SPLICE ;
2014-04-14 09:56:58 -04:00
# endif
# if defined(USE_GETADDRINFO)
else if ( * flag = = ' d ' & & flag [ 1 ] = = ' G ' )
global . tune . options & = ~ GTUNE_USE_GAI ;
2016-09-12 17:42:20 -04:00
# endif
# if defined(SO_REUSEPORT)
else if ( * flag = = ' d ' & & flag [ 1 ] = = ' R ' )
global . tune . options & = ~ GTUNE_USE_REUSEPORT ;
2006-06-25 20:48:02 -04:00
# endif
2014-01-29 06:24:34 -05:00
else if ( * flag = = ' d ' & & flag [ 1 ] = = ' V ' )
global . ssl_server_verify = SSL_SERVER_VERIFY_NONE ;
2006-06-25 20:48:02 -04:00
else if ( * flag = = ' V ' )
arg_mode | = MODE_VERBOSE ;
else if ( * flag = = ' d ' & & flag [ 1 ] = = ' b ' )
arg_mode | = MODE_FOREGROUND ;
2012-05-08 09:40:42 -04:00
else if ( * flag = = ' d ' & & flag [ 1 ] = = ' M ' )
mem_poison_byte = flag [ 2 ] ? strtol ( flag + 2 , NULL , 0 ) : ' P ' ;
2016-11-07 15:03:16 -05:00
else if ( * flag = = ' d ' & & flag [ 1 ] = = ' r ' )
global . tune . options | = GTUNE_RESOLVE_DONTFAIL ;
2006-06-25 20:48:02 -04:00
else if ( * flag = = ' d ' )
arg_mode | = MODE_DEBUG ;
else if ( * flag = = ' c ' )
arg_mode | = MODE_CHECK ;
2017-06-01 11:38:50 -04:00
else if ( * flag = = ' D ' )
2009-05-18 10:29:51 -04:00
arg_mode | = MODE_DAEMON ;
2017-11-20 09:58:35 -05:00
else if ( * flag = = ' W ' & & flag [ 1 ] = = ' s ' ) {
2017-11-21 06:39:34 -05:00
arg_mode | = MODE_MWORKER | MODE_FOREGROUND ;
2017-11-20 09:58:35 -05:00
# if defined(USE_SYSTEMD)
global . tune . options | = GTUNE_USE_SYSTEMD ;
# else
2017-11-24 10:50:31 -05:00
ha_alert ( " master-worker mode with systemd support (-Ws) requested, but not compiled. Use master-worker mode (-W) if you are not using Type=notify in your unit file or recompile with USE_SYSTEMD=1. \n \n " ) ;
2017-11-20 09:58:35 -05:00
usage ( progname ) ;
# endif
}
2017-06-01 11:38:50 -04:00
else if ( * flag = = ' W ' )
arg_mode | = MODE_MWORKER ;
2006-06-25 20:48:02 -04:00
else if ( * flag = = ' q ' )
arg_mode | = MODE_QUIET ;
2017-04-05 16:33:04 -04:00
else if ( * flag = = ' x ' ) {
2017-06-19 09:57:55 -04:00
if ( argc < = 1 | | argv [ 1 ] [ 0 ] = = ' - ' ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " Unix socket path expected with the -x flag \n \n " ) ;
2017-06-19 09:57:55 -04:00
usage ( progname ) ;
2017-04-05 16:33:04 -04:00
}
2017-06-19 10:37:19 -04:00
if ( old_unixsocket )
2017-11-24 10:50:31 -05:00
ha_warning ( " -x option already set, overwriting the value \n " ) ;
2017-04-05 16:33:04 -04:00
old_unixsocket = argv [ 1 ] ;
2017-06-19 10:37:19 -04:00
2017-04-05 16:33:04 -04:00
argv + + ;
argc - - ;
}
2018-10-26 08:47:36 -04:00
else if ( * flag = = ' S ' ) {
struct wordlist * c ;
if ( argc < = 1 | | argv [ 1 ] [ 0 ] = = ' - ' ) {
ha_alert ( " Socket and optional bind parameters expected with the -S flag \n " ) ;
usage ( progname ) ;
}
if ( ( c = malloc ( sizeof ( * c ) ) ) = = NULL | | ( c - > s = strdup ( argv [ 1 ] ) ) = = NULL ) {
ha_alert ( " Cannot allocate memory \n " ) ;
exit ( EXIT_FAILURE ) ;
}
LIST_ADD ( & mworker_cli_conf , & c - > list ) ;
argv + + ;
argc - - ;
}
2006-06-25 20:48:02 -04:00
else if ( * flag = = ' s ' & & ( flag [ 1 ] = = ' f ' | | flag [ 1 ] = = ' t ' ) ) {
/* list of pids to finish ('f') or terminate ('t') */
if ( flag [ 1 ] = = ' f ' )
oldpids_sig = SIGUSR1 ; /* finish then exit */
else
oldpids_sig = SIGTERM ; /* terminate immediately */
2015-10-08 05:32:32 -04:00
while ( argc > 1 & & argv [ 1 ] [ 0 ] ! = ' - ' ) {
2018-02-05 18:15:44 -05:00
char * endptr = NULL ;
2015-10-08 05:32:32 -04:00
oldpids = realloc ( oldpids , ( nb_oldpids + 1 ) * sizeof ( int ) ) ;
if ( ! oldpids ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " Cannot allocate old pid : out of memory. \n " ) ;
2015-10-08 05:32:32 -04:00
exit ( 1 ) ;
2006-06-25 20:48:02 -04:00
}
2015-10-08 05:32:32 -04:00
argc - - ; argv + + ;
2018-02-05 18:15:44 -05:00
errno = 0 ;
oldpids [ nb_oldpids ] = strtol ( * argv , & endptr , 10 ) ;
if ( errno ) {
ha_alert ( " -%2s option: failed to parse {%s}: %s \n " ,
flag ,
* argv , strerror ( errno ) ) ;
exit ( 1 ) ;
} else if ( endptr & & strlen ( endptr ) ) {
while ( isspace ( * endptr ) ) endptr + + ;
2018-02-17 14:53:11 -05:00
if ( * endptr ! = 0 ) {
2018-02-05 18:15:44 -05:00
ha_alert ( " -%2s option: some bytes unconsumed in PID list {%s} \n " ,
flag , endptr ) ;
exit ( 1 ) ;
2018-02-17 14:53:11 -05:00
}
2018-02-05 18:15:44 -05:00
}
2015-10-08 05:32:32 -04:00
if ( oldpids [ nb_oldpids ] < = 0 )
usage ( progname ) ;
nb_oldpids + + ;
2006-06-25 20:48:02 -04:00
}
}
2015-10-08 05:58:48 -04:00
else if ( flag [ 0 ] = = ' - ' & & flag [ 1 ] = = 0 ) { /* "--" */
/* now that's a cfgfile list */
argv + + ; argc - - ;
while ( argc > 0 ) {
2016-05-13 17:52:55 -04:00
if ( ! list_append_word ( & cfg_cfgfiles , * argv , & err_msg ) ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " Cannot load configuration file/directory %s : %s \n " ,
* argv ,
err_msg ) ;
2015-10-08 05:58:48 -04:00
exit ( 1 ) ;
}
argv + + ; argc - - ;
}
break ;
}
2006-06-25 20:48:02 -04:00
else { /* >=2 args */
argv + + ; argc - - ;
if ( argc = = 0 )
2011-09-10 13:20:23 -04:00
usage ( progname ) ;
2006-06-25 20:48:02 -04:00
switch ( * flag ) {
2011-09-10 13:26:56 -04:00
case ' C ' : change_dir = * argv ; break ;
2006-06-25 20:48:02 -04:00
case ' n ' : cfg_maxconn = atol ( * argv ) ; break ;
2015-12-14 06:46:07 -05:00
case ' m ' : global . rlimit_memmax_all = atol ( * argv ) ; break ;
2006-06-25 20:48:02 -04:00
case ' N ' : cfg_maxpconn = atol ( * argv ) ; break ;
2018-04-17 10:46:13 -04:00
case ' L ' :
strncpy ( localpeer , * argv , sizeof ( localpeer ) - 1 ) ;
setenv ( " HAPROXY_LOCALPEER " , localpeer , 1 ) ;
break ;
2009-06-22 10:02:30 -04:00
case ' f ' :
2016-05-13 17:52:55 -04:00
if ( ! list_append_word ( & cfg_cfgfiles , * argv , & err_msg ) ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " Cannot load configuration file/directory %s : %s \n " ,
* argv ,
err_msg ) ;
2009-06-22 10:02:30 -04:00
exit ( 1 ) ;
}
break ;
2006-06-25 20:48:02 -04:00
case ' p ' : cfg_pidfile = * argv ; break ;
2011-09-10 13:20:23 -04:00
default : usage ( progname ) ;
2006-06-25 20:48:02 -04:00
}
}
}
else
2011-09-10 13:20:23 -04:00
usage ( progname ) ;
2006-06-25 20:48:02 -04:00
argv + + ; argc - - ;
}
2017-10-24 07:53:54 -04:00
global . mode | = ( arg_mode & ( MODE_DAEMON | MODE_MWORKER | MODE_FOREGROUND | MODE_VERBOSE
| MODE_QUIET | MODE_CHECK | MODE_DEBUG ) ) ;
2006-06-25 20:48:02 -04:00
2018-11-21 09:48:31 -05:00
if ( getenv ( " HAPROXY_MWORKER_WAIT_ONLY " ) ) {
2017-06-01 11:38:52 -04:00
unsetenv ( " HAPROXY_MWORKER_WAIT_ONLY " ) ;
2018-11-21 09:48:31 -05:00
global . mode | = MODE_MWORKER_WAIT ;
global . mode & = ~ MODE_MWORKER ;
2017-06-01 11:38:52 -04:00
}
if ( ( global . mode & MODE_MWORKER ) & & ( getenv ( " HAPROXY_MWORKER_REEXEC " ) ! = NULL ) ) {
atexit_flag = 1 ;
atexit ( reexec_on_failure ) ;
}
2011-09-10 13:26:56 -04:00
if ( change_dir & & chdir ( change_dir ) < 0 ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " Could not change to directory %s : %s \n " , change_dir , strerror ( errno ) ) ;
2011-09-10 13:26:56 -04:00
exit ( 1 ) ;
}
2006-06-25 20:48:02 -04:00
global . maxsock = 10 ; /* reserve 10 fds ; will be incremented by socket eaters */
2009-06-22 09:48:36 -04:00
init_default_instance ( ) ;
2018-11-21 09:48:31 -05:00
/* in wait mode, we don't try to read the configuration files */
if ( ! ( global . mode & MODE_MWORKER_WAIT ) ) {
2009-12-06 07:10:44 -05:00
2018-11-21 09:48:31 -05:00
/* handle cfgfiles that are actually directories */
cfgfiles_expand_directories ( ) ;
if ( LIST_ISEMPTY ( & cfg_cfgfiles ) )
usage ( progname ) ;
list_for_each_entry ( wl , & cfg_cfgfiles , list ) {
int ret ;
ret = readcfgfile ( wl - > s ) ;
if ( ret = = - 1 ) {
ha_alert ( " Could not open configuration file %s : %s \n " ,
wl - > s , strerror ( errno ) ) ;
exit ( 1 ) ;
}
if ( ret & ( ERR_ABORT | ERR_FATAL ) )
ha_alert ( " Error(s) found in configuration file : %s \n " , wl - > s ) ;
err_code | = ret ;
if ( err_code & ERR_ABORT )
exit ( 1 ) ;
2009-12-06 07:10:44 -05:00
}
2007-10-14 17:40:01 -04:00
2018-11-21 09:48:31 -05:00
/* do not try to resolve arguments nor to spot inconsistencies when
* the configuration contains fatal errors caused by files not found
* or failed memory allocations .
*/
if ( err_code & ( ERR_ABORT | ERR_FATAL ) ) {
ha_alert ( " Fatal errors found in configuration. \n " ) ;
exit ( 1 ) ;
}
MEDIUM: config: don't check config validity when there are fatal errors
Overall we do have an issue with the severity of a number of errors. Most
fatal errors are reported with ERR_FATAL (which prevents startup) and not
ERR_ABORT (which stops parsing ASAP), but check_config_validity() is still
called on ERR_FATAL, and will most of the time report bogus errors. This
is what caused smp_resolve_args() to be called on a number of unparsable
ACLs, and it also is what reports incorrect ordering or unresolvable
section names when certain entries could not be properly parsed.
This patch stops this domino effect by simply aborting before trying to
further check and resolve the configuration when it's already know that
there are fatal errors.
A concrete example comes from this config :
userlist users :
user foo insecure-password bar
listen foo
bind :1234
mode htttp
timeout client 10S
timeout server 10s
timeout connect 10s
stats uri /stats
stats http-request auth unless { http_auth(users) }
http-request redirect location /index.html if { path / }
It contains a colon after the userlist name, a typo in the client timeout value,
another one in "mode http" which cause some other configuration elements not to
be properly handled.
Previously it would confusingly report :
[ALERT] 108/114851 (20224) : parsing [err-report.cfg:1] : 'userlist' cannot handle unexpected argument ':'.
[ALERT] 108/114851 (20224) : parsing [err-report.cfg:6] : unknown proxy mode 'htttp'.
[ALERT] 108/114851 (20224) : parsing [err-report.cfg:7] : unexpected character 'S' in 'timeout client'
[ALERT] 108/114851 (20224) : Error(s) found in configuration file : err-report.cfg
[ALERT] 108/114851 (20224) : parsing [err-report.cfg:11] : unable to find userlist 'users' referenced in arg 1 of ACL keyword 'http_auth' in proxy 'foo'.
[WARNING] 108/114851 (20224) : config : missing timeouts for proxy 'foo'.
| While not properly invalid, you will certainly encounter various problems
| with such a configuration. To fix this, please ensure that all following
| timeouts are set to a non-zero value: 'client', 'connect', 'server'.
[WARNING] 108/114851 (20224) : config : 'stats' statement ignored for proxy 'foo' as it requires HTTP mode.
[WARNING] 108/114851 (20224) : config : 'http-request' rules ignored for proxy 'foo' as they require HTTP mode.
[ALERT] 108/114851 (20224) : Fatal errors found in configuration.
The "requires HTTP mode" errors are just pollution resulting from the
improper spelling of this mode earlier. The unresolved reference to the
userlist is caused by the extra colon on the declaration, and the warning
regarding the missing timeouts is caused by the wrong character.
Now it more accurately reports :
[ALERT] 108/114900 (20225) : parsing [err-report.cfg:1] : 'userlist' cannot handle unexpected argument ':'.
[ALERT] 108/114900 (20225) : parsing [err-report.cfg:6] : unknown proxy mode 'htttp'.
[ALERT] 108/114900 (20225) : parsing [err-report.cfg:7] : unexpected character 'S' in 'timeout client'
[ALERT] 108/114900 (20225) : Error(s) found in configuration file : err-report.cfg
[ALERT] 108/114900 (20225) : Fatal errors found in configuration.
Despite not really a fix, this patch should be backported at least to 1.7,
possibly even 1.6, and 1.5 since it hardens the config parser against
certain bad situations like the recently reported use-after-free and the
last null dereference.
2017-04-19 05:24:07 -04:00
}
2018-10-26 08:47:30 -04:00
if ( global . mode & MODE_MWORKER ) {
int proc ;
2018-11-19 12:46:18 -05:00
struct mworker_proc * tmproc ;
if ( getenv ( " HAPROXY_MWORKER_REEXEC " ) = = NULL ) {
tmproc = malloc ( sizeof ( * tmproc ) ) ;
if ( ! tmproc ) {
ha_alert ( " Cannot allocate process structures. \n " ) ;
exit ( EXIT_FAILURE ) ;
}
tmproc - > type = ' m ' ; /* master */
tmproc - > reloads = 0 ;
tmproc - > relative_pid = 0 ;
tmproc - > pid = pid ;
tmproc - > timestamp = start_date . tv_sec ;
tmproc - > ipc_fd [ 0 ] = - 1 ;
tmproc - > ipc_fd [ 1 ] = - 1 ;
proc_self = tmproc ;
LIST_ADDQ ( & proc_list , & tmproc - > list ) ;
}
2018-10-26 08:47:30 -04:00
for ( proc = 0 ; proc < global . nbproc ; proc + + ) {
tmproc = malloc ( sizeof ( * tmproc ) ) ;
if ( ! tmproc ) {
ha_alert ( " Cannot allocate process structures. \n " ) ;
exit ( EXIT_FAILURE ) ;
}
2018-11-19 12:46:18 -05:00
tmproc - > type = ' w ' ; /* worker */
2018-10-26 08:47:30 -04:00
tmproc - > pid = - 1 ;
tmproc - > reloads = 0 ;
2018-11-19 12:46:17 -05:00
tmproc - > timestamp = - 1 ;
2018-10-26 08:47:30 -04:00
tmproc - > relative_pid = 1 + proc ;
2018-11-06 11:37:12 -05:00
tmproc - > ipc_fd [ 0 ] = - 1 ;
tmproc - > ipc_fd [ 1 ] = - 1 ;
2018-10-26 08:47:30 -04:00
if ( mworker_cli_sockpair_new ( tmproc , proc ) < 0 ) {
exit ( EXIT_FAILURE ) ;
}
LIST_ADDQ ( & proc_list , & tmproc - > list ) ;
}
2018-11-21 09:48:31 -05:00
}
if ( global . mode & ( MODE_MWORKER | MODE_MWORKER_WAIT ) ) {
struct wordlist * it , * c ;
2018-10-26 08:47:33 -04:00
mworker_env_to_proc_list ( ) ; /* get the info of the children in the env */
2018-10-26 08:47:35 -04:00
2018-10-26 08:47:36 -04:00
2018-11-06 11:37:12 -05:00
if ( ! LIST_ISEMPTY ( & mworker_cli_conf ) ) {
2018-10-26 08:47:36 -04:00
2018-11-06 11:37:12 -05:00
if ( mworker_cli_proxy_create ( ) < 0 ) {
2018-10-26 08:47:36 -04:00
ha_alert ( " Can't create the master's CLI. \n " ) ;
exit ( EXIT_FAILURE ) ;
}
2018-11-06 11:37:12 -05:00
list_for_each_entry_safe ( c , it , & mworker_cli_conf , list ) {
if ( mworker_cli_proxy_new_listener ( c - > s ) < 0 ) {
ha_alert ( " Can't create the master's CLI. \n " ) ;
exit ( EXIT_FAILURE ) ;
}
LIST_DEL ( & c - > list ) ;
free ( c - > s ) ;
free ( c ) ;
}
}
2018-10-26 08:47:30 -04:00
}
2014-03-11 09:29:22 -04:00
pattern_finalize_config ( ) ;
2009-07-23 07:36:36 -04:00
err_code | = check_config_validity ( ) ;
if ( err_code & ( ERR_ABORT | ERR_FATAL ) ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " Fatal errors found in configuration. \n " ) ;
2009-06-22 09:48:36 -04:00
exit ( 1 ) ;
}
2006-06-25 20:48:02 -04:00
2015-12-14 06:46:07 -05:00
/* recompute the amount of per-process memory depending on nbproc and
* the shared SSL cache size ( allowed to exist in all processes ) .
*/
if ( global . rlimit_memmax_all ) {
# if defined (USE_OPENSSL) && !defined(USE_PRIVATE_CACHE)
int64_t ssl_cache_bytes = global . tune . sslcachesize * 200LL ;
global . rlimit_memmax =
( ( ( ( int64_t ) global . rlimit_memmax_all * 1048576LL ) -
ssl_cache_bytes ) / global . nbproc +
ssl_cache_bytes + 1048575LL ) / 1048576LL ;
# else
global . rlimit_memmax = global . rlimit_memmax_all / global . nbproc ;
# endif
}
2014-11-17 09:11:45 -05:00
# ifdef CONFIG_HAP_NS
err_code | = netns_init ( ) ;
if ( err_code & ( ERR_ABORT | ERR_FATAL ) ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " Failed to initialize namespace support. \n " ) ;
2014-11-17 09:11:45 -05:00
exit ( 1 ) ;
}
# endif
2016-11-02 10:33:15 -04:00
/* Apply server states */
apply_server_state ( ) ;
2017-11-24 10:54:05 -05:00
for ( px = proxies_list ; px ; px = px - > next )
2016-11-02 10:33:15 -04:00
srv_compute_all_admin_states ( px ) ;
2016-11-02 10:34:05 -04:00
/* Apply servers' configured address */
err_code | = srv_init_addr ( ) ;
if ( err_code & ( ERR_ABORT | ERR_FATAL ) ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " Failed to initialize server(s) addr. \n " ) ;
2016-11-02 10:34:05 -04:00
exit ( 1 ) ;
}
2006-06-25 20:48:02 -04:00
if ( global . mode & MODE_CHECK ) {
2012-02-02 11:48:18 -05:00
struct peers * pr ;
struct proxy * px ;
2017-07-13 03:07:09 -04:00
for ( pr = cfg_peers ; pr ; pr = pr - > next )
2012-02-02 11:48:18 -05:00
if ( pr - > peers_fe )
break ;
2017-11-24 10:54:05 -05:00
for ( px = proxies_list ; px ; px = px - > next )
2012-09-20 10:48:07 -04:00
if ( px - > state = = PR_STNEW & & ! LIST_ISEMPTY ( & px - > conf . listeners ) )
2012-02-02 11:48:18 -05:00
break ;
if ( pr | | px ) {
/* At least one peer or one listener has been found */
qfprintf ( stdout , " Configuration file is valid \n " ) ;
exit ( 0 ) ;
}
qfprintf ( stdout , " Configuration file has no error but will not start (no listener) => exit(2). \n " ) ;
exit ( 2 ) ;
2006-06-25 20:48:02 -04:00
}
2017-09-27 08:59:38 -04:00
global_listener_queue_task = task_new ( MAX_THREADS_MASK ) ;
2011-08-01 14:57:55 -04:00
if ( ! global_listener_queue_task ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " Out of memory when initializing global task \n " ) ;
2011-08-01 14:57:55 -04:00
exit ( 1 ) ;
}
/* very simple initialization, users will queue the task if needed */
global_listener_queue_task - > context = NULL ; /* not even a context! */
global_listener_queue_task - > process = manage_global_listener_queue ;
2012-08-27 18:06:31 -04:00
/* now we know the buffer size, we can initialize the channels and buffers */
2012-10-12 17:49:43 -04:00
init_buffer ( ) ;
2009-09-23 17:37:52 -04:00
2016-12-21 13:57:00 -05:00
list_for_each_entry ( pcf , & post_check_list , list ) {
err_code | = pcf - > fct ( ) ;
if ( err_code & ( ERR_ABORT | ERR_FATAL ) )
exit ( 1 ) ;
}
2006-06-25 20:48:02 -04:00
if ( cfg_maxconn > 0 )
global . maxconn = cfg_maxconn ;
if ( cfg_pidfile ) {
2008-08-03 06:19:50 -04:00
free ( global . pidfile ) ;
2006-06-25 20:48:02 -04:00
global . pidfile = strdup ( cfg_pidfile ) ;
}
2015-01-15 15:45:22 -05:00
/* Now we want to compute the maxconn and possibly maxsslconn values.
* It ' s a bit tricky . If memmax is not set , maxconn defaults to
* DEFAULT_MAXCONN and maxsslconn defaults to DEFAULT_MAXSSLCONN .
*
* If memmax is set , then it depends on which values are set . If
* maxsslconn is set , we use memmax to determine how many cleartext
* connections may be added , and set maxconn to the sum of the two .
* If maxconn is set and not maxsslconn , maxsslconn is computed from
* the remaining amount of memory between memmax and the cleartext
* connections . If neither are set , then it is considered that all
* connections are SSL - capable , and maxconn is computed based on this ,
* then maxsslconn accordingly . We need to know if SSL is used on the
* frontends , backends , or both , because when it ' s used on both sides ,
* we need twice the value for maxsslconn , but we only count the
* handshake once since it is not performed on the two sides at the
* same time ( frontend - side is terminated before backend - side begins ) .
* The SSL stack is supposed to have filled ssl_session_cost and
2015-01-28 13:03:21 -05:00
* ssl_handshake_cost during its initialization . In any case , if
* SYSTEM_MAXCONN is set , we still enforce it as an upper limit for
* maxconn in order to protect the system .
2015-01-15 15:45:22 -05:00
*/
if ( ! global . rlimit_memmax ) {
if ( global . maxconn = = 0 ) {
global . maxconn = DEFAULT_MAXCONN ;
if ( global . mode & ( MODE_VERBOSE | MODE_DEBUG ) )
fprintf ( stderr , " Note: setting global.maxconn to %d. \n " , global . maxconn ) ;
}
}
# ifdef USE_OPENSSL
else if ( ! global . maxconn & & ! global . maxsslconn & &
( global . ssl_used_frontend | | global . ssl_used_backend ) ) {
/* memmax is set, compute everything automatically. Here we want
* to ensure that all SSL connections will be served . We take
* care of the number of sides where SSL is used , and consider
* the worst case : SSL used on both sides and doing a handshake
* simultaneously . Note that we can ' t have more than maxconn
* handshakes at a time by definition , so for the worst case of
* two SSL conns per connection , we count a single handshake .
*/
int sides = ! ! global . ssl_used_frontend + ! ! global . ssl_used_backend ;
int64_t mem = global . rlimit_memmax * 1048576ULL ;
mem - = global . tune . sslcachesize * 200 ; // about 200 bytes per SSL cache entry
mem - = global . maxzlibmem ;
mem = mem * MEM_USABLE_RATIO ;
global . maxconn = mem /
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
( ( STREAM_MAX_COST + 2 * global . tune . bufsize ) + // stream + 2 buffers per stream
2015-01-15 15:45:22 -05:00
sides * global . ssl_session_max_cost + // SSL buffers, one per side
global . ssl_handshake_max_cost ) ; // 1 handshake per connection max
global . maxconn = round_2dig ( global . maxconn ) ;
2015-01-28 13:03:21 -05:00
# ifdef SYSTEM_MAXCONN
if ( global . maxconn > DEFAULT_MAXCONN )
global . maxconn = DEFAULT_MAXCONN ;
# endif /* SYSTEM_MAXCONN */
2015-01-15 15:45:22 -05:00
global . maxsslconn = sides * global . maxconn ;
if ( global . mode & ( MODE_VERBOSE | MODE_DEBUG ) )
fprintf ( stderr , " Note: setting global.maxconn to %d and global.maxsslconn to %d. \n " ,
global . maxconn , global . maxsslconn ) ;
}
else if ( ! global . maxsslconn & &
( global . ssl_used_frontend | | global . ssl_used_backend ) ) {
/* memmax and maxconn are known, compute maxsslconn automatically.
* maxsslconn being forced , we don ' t know how many of it will be
* on each side if both sides are being used . The worst case is
* when all connections use only one SSL instance because
* handshakes may be on two sides at the same time .
*/
int sides = ! ! global . ssl_used_frontend + ! ! global . ssl_used_backend ;
int64_t mem = global . rlimit_memmax * 1048576ULL ;
int64_t sslmem ;
mem - = global . tune . sslcachesize * 200 ; // about 200 bytes per SSL cache entry
mem - = global . maxzlibmem ;
mem = mem * MEM_USABLE_RATIO ;
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
sslmem = mem - global . maxconn * ( int64_t ) ( STREAM_MAX_COST + 2 * global . tune . bufsize ) ;
2015-01-15 15:45:22 -05:00
global . maxsslconn = sslmem / ( global . ssl_session_max_cost + global . ssl_handshake_max_cost ) ;
global . maxsslconn = round_2dig ( global . maxsslconn ) ;
if ( sslmem < = 0 | | global . maxsslconn < sides ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " Cannot compute the automatic maxsslconn because global.maxconn is already too "
" high for the global.memmax value (%d MB). The absolute maximum possible value "
" without SSL is %d, but %d was found and SSL is in use. \n " ,
global . rlimit_memmax ,
( int ) ( mem / ( STREAM_MAX_COST + 2 * global . tune . bufsize ) ) ,
global . maxconn ) ;
2015-01-15 15:45:22 -05:00
exit ( 1 ) ;
}
if ( global . maxsslconn > sides * global . maxconn )
global . maxsslconn = sides * global . maxconn ;
if ( global . mode & ( MODE_VERBOSE | MODE_DEBUG ) )
fprintf ( stderr , " Note: setting global.maxsslconn to %d \n " , global . maxsslconn ) ;
}
# endif
else if ( ! global . maxconn ) {
/* memmax and maxsslconn are known/unused, compute maxconn automatically */
int sides = ! ! global . ssl_used_frontend + ! ! global . ssl_used_backend ;
int64_t mem = global . rlimit_memmax * 1048576ULL ;
int64_t clearmem ;
if ( global . ssl_used_frontend | | global . ssl_used_backend )
mem - = global . tune . sslcachesize * 200 ; // about 200 bytes per SSL cache entry
mem - = global . maxzlibmem ;
mem = mem * MEM_USABLE_RATIO ;
clearmem = mem ;
if ( sides )
clearmem - = ( global . ssl_session_max_cost + global . ssl_handshake_max_cost ) * ( int64_t ) global . maxsslconn ;
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
global . maxconn = clearmem / ( STREAM_MAX_COST + 2 * global . tune . bufsize ) ;
2015-01-15 15:45:22 -05:00
global . maxconn = round_2dig ( global . maxconn ) ;
2015-01-28 13:03:21 -05:00
# ifdef SYSTEM_MAXCONN
if ( global . maxconn > DEFAULT_MAXCONN )
global . maxconn = DEFAULT_MAXCONN ;
# endif /* SYSTEM_MAXCONN */
2015-01-15 15:45:22 -05:00
if ( clearmem < = 0 | | ! global . maxconn ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " Cannot compute the automatic maxconn because global.maxsslconn is already too "
" high for the global.memmax value (%d MB). The absolute maximum possible value "
" is %d, but %d was found. \n " ,
global . rlimit_memmax ,
( int ) ( mem / ( global . ssl_session_max_cost + global . ssl_handshake_max_cost ) ) ,
global . maxsslconn ) ;
2015-01-15 15:45:22 -05:00
exit ( 1 ) ;
}
if ( global . mode & ( MODE_VERBOSE | MODE_DEBUG ) ) {
if ( sides & & global . maxsslconn > sides * global . maxconn ) {
fprintf ( stderr , " Note: global.maxsslconn is forced to %d which causes global.maxconn "
" to be limited to %d. Better reduce global.maxsslconn to get more "
" room for extra connections. \n " , global . maxsslconn , global . maxconn ) ;
}
fprintf ( stderr , " Note: setting global.maxconn to %d \n " , global . maxconn ) ;
}
}
2006-06-25 20:48:02 -04:00
2009-01-18 15:44:07 -05:00
if ( ! global . maxpipes ) {
/* maxpipes not specified. Count how many frontends and backends
* may be using splicing , and bound that to maxconn .
*/
struct proxy * cur ;
int nbfe = 0 , nbbe = 0 ;
2017-11-24 10:54:05 -05:00
for ( cur = proxies_list ; cur ; cur = cur - > next ) {
2009-01-18 15:44:07 -05:00
if ( cur - > options2 & ( PR_O2_SPLIC_ANY ) ) {
if ( cur - > cap & PR_CAP_FE )
nbfe + = cur - > maxconn ;
if ( cur - > cap & PR_CAP_BE )
2009-01-25 04:42:05 -05:00
nbbe + = cur - > fullconn ? cur - > fullconn : global . maxconn ;
2009-01-18 15:44:07 -05:00
}
}
global . maxpipes = MAX ( nbfe , nbbe ) ;
if ( global . maxpipes > global . maxconn )
global . maxpipes = global . maxconn ;
2009-01-25 08:06:58 -05:00
global . maxpipes / = 4 ;
2009-01-18 15:44:07 -05:00
}
2011-09-07 08:26:33 -04:00
global . hardmaxconn = global . maxconn ; /* keep this max value */
2006-06-25 20:48:02 -04:00
global . maxsock + = global . maxconn * 2 ; /* each connection needs two sockets */
2009-01-18 14:39:42 -05:00
global . maxsock + = global . maxpipes * 2 ; /* each pipe needs two FDs */
2017-12-06 07:51:49 -05:00
/* compute fd used by async engines */
if ( global . ssl_used_async_engines ) {
int sides = ! ! global . ssl_used_frontend + ! ! global . ssl_used_backend ;
global . maxsock + = global . maxconn * sides * global . ssl_used_async_engines ;
}
2006-06-25 20:48:02 -04:00
2011-09-07 12:00:47 -04:00
if ( global . stats_fe )
global . maxsock + = global . stats_fe - > maxconn ;
2017-07-13 03:07:09 -04:00
if ( cfg_peers ) {
2011-09-07 12:00:47 -04:00
/* peers also need to bypass global maxconn */
2017-07-13 03:07:09 -04:00
struct peers * p = cfg_peers ;
2011-09-07 12:00:47 -04:00
2017-07-13 03:07:09 -04:00
for ( p = cfg_peers ; p ; p = p - > next )
2011-09-07 12:00:47 -04:00
if ( p - > peers_fe )
global . maxsock + = p - > peers_fe - > maxconn ;
}
2007-06-03 11:16:49 -04:00
if ( global . tune . maxpollevents < = 0 )
global . tune . maxpollevents = MAX_POLL_EVENTS ;
2018-05-24 12:59:04 -04:00
if ( global . tune . runqueue_depth < = 0 )
global . tune . runqueue_depth = RUNQUEUE_DEPTH ;
2009-03-21 15:43:57 -04:00
if ( global . tune . recv_enough = = 0 )
global . tune . recv_enough = MIN_RECV_AT_ONCE_ENOUGH ;
2015-09-28 07:53:23 -04:00
if ( global . tune . maxrewrite < 0 )
global . tune . maxrewrite = MAXREWRITE ;
2009-08-17 01:23:33 -04:00
if ( global . tune . maxrewrite > = global . tune . bufsize / 2 )
global . tune . maxrewrite = global . tune . bufsize / 2 ;
2006-06-25 20:48:02 -04:00
if ( arg_mode & ( MODE_DEBUG | MODE_FOREGROUND ) ) {
/* command line debug mode inhibits configuration mode */
2017-06-01 11:38:50 -04:00
global . mode & = ~ ( MODE_DAEMON | MODE_QUIET ) ;
2012-10-26 10:04:28 -04:00
global . mode | = ( arg_mode & ( MODE_DEBUG | MODE_FOREGROUND ) ) ;
2006-06-25 20:48:02 -04:00
}
2012-10-26 10:04:28 -04:00
2017-06-01 11:38:50 -04:00
if ( arg_mode & MODE_DAEMON ) {
2012-10-26 10:04:28 -04:00
/* command line daemon mode inhibits foreground and debug modes mode */
global . mode & = ~ ( MODE_DEBUG | MODE_FOREGROUND ) ;
2017-06-01 11:38:50 -04:00
global . mode | = arg_mode & MODE_DAEMON ;
2012-10-26 10:04:28 -04:00
}
global . mode | = ( arg_mode & ( MODE_QUIET | MODE_VERBOSE ) ) ;
2006-06-25 20:48:02 -04:00
2017-06-01 11:38:50 -04:00
if ( ( global . mode & MODE_DEBUG ) & & ( global . mode & ( MODE_DAEMON | MODE_QUIET ) ) ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " <debug> mode incompatible with <quiet> and <daemon>. Keeping <debug> only. \n " ) ;
2017-06-01 11:38:50 -04:00
global . mode & = ~ ( MODE_DAEMON | MODE_QUIET ) ;
2006-06-25 20:48:02 -04:00
}
2017-06-01 11:38:50 -04:00
if ( ( global . nbproc > 1 ) & & ! ( global . mode & ( MODE_DAEMON | MODE_MWORKER ) ) ) {
2006-06-25 20:48:02 -04:00
if ( ! ( global . mode & ( MODE_FOREGROUND | MODE_DEBUG ) ) )
2017-11-24 10:50:31 -05:00
ha_warning ( " <nbproc> is only meaningful in daemon mode or master-worker mode. Setting limit to 1 process. \n " ) ;
2006-06-25 20:48:02 -04:00
global . nbproc = 1 ;
}
if ( global . nbproc < 1 )
global . nbproc = 1 ;
2017-08-29 09:37:10 -04:00
if ( global . nbthread < 1 )
global . nbthread = 1 ;
2017-08-29 10:46:57 -04:00
/* Realloc trash buffers because global.tune.bufsize may have changed */
2017-10-27 07:53:47 -04:00
if ( ! init_trash_buffers ( 0 ) ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " failed to initialize trash buffers. \n " ) ;
2017-08-29 10:46:57 -04:00
exit ( 1 ) ;
}
2017-11-14 16:02:30 -05:00
if ( ! init_log_buffers ( ) ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " failed to initialize log buffers. \n " ) ;
2017-11-14 16:02:30 -05:00
exit ( 1 ) ;
}
2007-04-15 18:25:25 -04:00
/*
* Note : we could register external pollers here .
* Built - in pollers have been registered before main ( ) .
*/
2007-04-08 10:39:58 -04:00
2009-01-25 09:42:27 -05:00
if ( ! ( global . tune . options & GTUNE_USE_KQUEUE ) )
2007-04-09 06:03:06 -04:00
disable_poller ( " kqueue " ) ;
2009-01-25 09:42:27 -05:00
if ( ! ( global . tune . options & GTUNE_USE_EPOLL ) )
2007-04-08 10:39:58 -04:00
disable_poller ( " epoll " ) ;
2009-01-25 09:42:27 -05:00
if ( ! ( global . tune . options & GTUNE_USE_POLL ) )
2007-04-08 10:39:58 -04:00
disable_poller ( " poll " ) ;
2009-01-25 09:42:27 -05:00
if ( ! ( global . tune . options & GTUNE_USE_SELECT ) )
2007-04-08 10:39:58 -04:00
disable_poller ( " select " ) ;
/* Note: we could disable any poller by name here */
2016-03-07 06:46:38 -05:00
if ( global . mode & ( MODE_VERBOSE | MODE_DEBUG ) ) {
2007-04-09 13:29:56 -04:00
list_pollers ( stderr ) ;
2016-03-07 06:46:38 -05:00
fprintf ( stderr , " \n " ) ;
list_filters ( stderr ) ;
}
2007-04-09 13:29:56 -04:00
2007-04-08 10:39:58 -04:00
if ( ! init_pollers ( ) ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " No polling mechanism available. \n "
" It is likely that haproxy was built with TARGET=generic and that FD_SETSIZE \n "
" is too low on this platform to support maxconn and the number of listeners \n "
" and servers. You should rebuild haproxy specifying your system using TARGET= \n "
" in order to support other polling systems (poll, epoll, kqueue) or reduce the \n "
" global maxconn setting to accommodate the system's limitation. For reference, \n "
" FD_SETSIZE=%d on this system, global.maxconn=%d resulting in a maximum of \n "
" %d file descriptors. You should thus reduce global.maxconn by %d. Also, \n "
" check build settings using 'haproxy -vv'. \n \n " ,
FD_SETSIZE , global . maxconn , global . maxsock , ( global . maxsock + 1 - FD_SETSIZE ) / 2 ) ;
2007-04-08 10:39:58 -04:00
exit ( 1 ) ;
}
2007-04-09 13:29:56 -04:00
if ( global . mode & ( MODE_VERBOSE | MODE_DEBUG ) ) {
printf ( " Using %s() as the polling mechanism. \n " , cur_poller . name ) ;
2007-04-08 10:39:58 -04:00
}
2009-10-02 16:51:14 -04:00
if ( ! global . node )
global . node = strdup ( hostname ) ;
2015-01-23 06:08:30 -05:00
if ( ! hlua_post_init ( ) )
exit ( 1 ) ;
2015-05-12 11:23:58 -04:00
2016-05-13 17:52:55 -04:00
free ( err_msg ) ;
2006-06-25 20:48:02 -04:00
}
2011-07-15 00:14:11 -04:00
static void deinit_acl_cond ( struct acl_cond * cond )
2011-07-15 00:14:09 -04:00
{
struct acl_term_suite * suite , * suiteb ;
struct acl_term * term , * termb ;
2011-07-15 00:14:11 -04:00
if ( ! cond )
return ;
list_for_each_entry_safe ( suite , suiteb , & cond - > suites , list ) {
list_for_each_entry_safe ( term , termb , & suite - > terms , list ) {
LIST_DEL ( & term - > list ) ;
free ( term ) ;
2011-07-15 00:14:09 -04:00
}
2011-07-15 00:14:11 -04:00
LIST_DEL ( & suite - > list ) ;
free ( suite ) ;
}
free ( cond ) ;
}
static void deinit_tcp_rules ( struct list * rules )
{
2015-08-04 13:35:46 -04:00
struct act_rule * trule , * truleb ;
2011-07-15 00:14:11 -04:00
list_for_each_entry_safe ( trule , truleb , rules , list ) {
2011-07-15 00:14:09 -04:00
LIST_DEL ( & trule - > list ) ;
2011-07-15 00:14:11 -04:00
deinit_acl_cond ( trule - > cond ) ;
2011-07-15 00:14:09 -04:00
free ( trule ) ;
}
}
2011-07-15 00:14:11 -04:00
static void deinit_stick_rules ( struct list * rules )
{
struct sticking_rule * rule , * ruleb ;
list_for_each_entry_safe ( rule , ruleb , rules , list ) {
LIST_DEL ( & rule - > list ) ;
deinit_acl_cond ( rule - > cond ) ;
2016-10-26 05:34:47 -04:00
release_sample_expr ( rule - > expr ) ;
2011-07-15 00:14:11 -04:00
free ( rule ) ;
}
}
2017-03-23 17:44:13 -04:00
void deinit ( void )
2006-06-25 20:48:02 -04:00
{
2017-11-24 10:54:05 -05:00
struct proxy * p = proxies_list , * p0 ;
2006-06-25 20:48:02 -04:00
struct cap_hdr * h , * h_next ;
struct server * s , * s_next ;
struct listener * l , * l_next ;
2007-06-16 18:36:03 -04:00
struct acl_cond * cond , * condb ;
struct hdr_exp * exp , * expb ;
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
struct acl * acl , * aclb ;
2008-05-31 07:53:23 -04:00
struct switching_rule * rule , * ruleb ;
2012-04-05 15:09:48 -04:00
struct server_rule * srule , * sruleb ;
2008-06-07 17:08:56 -04:00
struct redirect_rule * rdr , * rdrb ;
2010-01-03 15:03:22 -05:00
struct wordlist * wl , * wlb ;
2010-01-28 12:10:50 -05:00
struct cond_wordlist * cwl , * cwlb ;
2008-05-31 07:53:23 -04:00
struct uri_auth * uap , * ua = NULL ;
2011-10-12 11:50:54 -04:00
struct logsrv * log , * logb ;
2012-02-08 10:37:49 -05:00
struct logformat_node * lf , * lfb ;
2012-09-13 11:54:29 -04:00
struct bind_conf * bind_conf , * bind_back ;
2016-12-21 12:43:10 -05:00
struct build_opts_str * bol , * bolb ;
2016-12-21 14:46:26 -05:00
struct post_deinit_fct * pdf ;
2007-06-16 18:36:03 -04:00
int i ;
2008-05-31 07:53:23 -04:00
2010-08-27 11:56:48 -04:00
deinit_signals ( ) ;
2006-06-25 20:48:02 -04:00
while ( p ) {
2012-10-04 02:01:43 -04:00
free ( p - > conf . file ) ;
2008-08-03 06:19:50 -04:00
free ( p - > id ) ;
free ( p - > check_req ) ;
free ( p - > cookie_name ) ;
free ( p - > cookie_domain ) ;
free ( p - > url_param_name ) ;
free ( p - > capture_name ) ;
free ( p - > monitor_uri ) ;
2011-07-15 00:14:08 -04:00
free ( p - > rdp_cookie_name ) ;
2013-04-12 12:13:46 -04:00
if ( p - > conf . logformat_string ! = default_http_log_format & &
p - > conf . logformat_string ! = default_tcp_log_format & &
p - > conf . logformat_string ! = clf_http_log_format )
free ( p - > conf . logformat_string ) ;
free ( p - > conf . lfs_file ) ;
free ( p - > conf . uniqueid_format_string ) ;
free ( p - > conf . uif_file ) ;
2013-10-02 05:10:11 -04:00
free ( p - > lbprm . map . srv ) ;
2006-06-25 20:48:02 -04:00
2015-09-25 13:17:44 -04:00
if ( p - > conf . logformat_sd_string ! = default_rfc5424_sd_log_format )
free ( p - > conf . logformat_sd_string ) ;
free ( p - > conf . lfsd_file ) ;
2008-08-03 06:19:50 -04:00
for ( i = 0 ; i < HTTP_ERR_SIZE ; i + + )
2009-09-27 07:23:20 -04:00
chunk_destroy ( & p - > errmsg [ i ] ) ;
2006-06-25 20:48:02 -04:00
2010-01-28 12:10:50 -05:00
list_for_each_entry_safe ( cwl , cwlb , & p - > req_add , list ) {
LIST_DEL ( & cwl - > list ) ;
free ( cwl - > s ) ;
free ( cwl ) ;
2010-01-03 15:03:22 -05:00
}
2006-06-25 20:48:02 -04:00
2010-01-28 12:10:50 -05:00
list_for_each_entry_safe ( cwl , cwlb , & p - > rsp_add , list ) {
LIST_DEL ( & cwl - > list ) ;
free ( cwl - > s ) ;
free ( cwl ) ;
2010-01-03 15:03:22 -05:00
}
2007-06-16 18:36:03 -04:00
2007-11-30 14:51:32 -05:00
list_for_each_entry_safe ( cond , condb , & p - > mon_fail_cond , list ) {
LIST_DEL ( & cond - > list ) ;
prune_acl_cond ( cond ) ;
free ( cond ) ;
}
2007-06-16 18:36:03 -04:00
for ( exp = p - > req_exp ; exp ! = NULL ; ) {
2008-05-31 07:53:23 -04:00
if ( exp - > preg ) {
2014-06-18 05:35:54 -04:00
regex_free ( exp - > preg ) ;
free ( exp - > preg ) ;
2008-05-31 07:53:23 -04:00
}
2015-05-26 06:18:29 -04:00
free ( ( char * ) exp - > replace ) ;
2007-06-16 18:36:03 -04:00
expb = exp ;
exp = exp - > next ;
free ( expb ) ;
}
for ( exp = p - > rsp_exp ; exp ! = NULL ; ) {
2008-05-31 07:53:23 -04:00
if ( exp - > preg ) {
2014-06-18 05:35:54 -04:00
regex_free ( exp - > preg ) ;
free ( exp - > preg ) ;
2008-05-31 07:53:23 -04:00
}
2015-05-26 06:18:29 -04:00
free ( ( char * ) exp - > replace ) ;
2007-06-16 18:36:03 -04:00
expb = exp ;
exp = exp - > next ;
free ( expb ) ;
}
2008-05-31 07:53:23 -04:00
/* build a list of unique uri_auths */
if ( ! ua )
ua = p - > uri_auth ;
else {
/* check if p->uri_auth is unique */
for ( uap = ua ; uap ; uap = uap - > next )
if ( uap = = p - > uri_auth )
break ;
2008-06-24 05:14:45 -04:00
if ( ! uap & & p - > uri_auth ) {
2008-05-31 07:53:23 -04:00
/* add it, if it is */
p - > uri_auth - > next = ua ;
ua = p - > uri_auth ;
}
}
2007-06-16 18:36:03 -04:00
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
list_for_each_entry_safe ( acl , aclb , & p - > acl , list ) {
LIST_DEL ( & acl - > list ) ;
prune_acl ( acl ) ;
free ( acl ) ;
}
2012-04-05 15:09:48 -04:00
list_for_each_entry_safe ( srule , sruleb , & p - > server_rules , list ) {
LIST_DEL ( & srule - > list ) ;
prune_acl_cond ( srule - > cond ) ;
free ( srule - > cond ) ;
free ( srule ) ;
}
2008-05-31 07:53:23 -04:00
list_for_each_entry_safe ( rule , ruleb , & p - > switching_rules , list ) {
LIST_DEL ( & rule - > list ) ;
2014-04-22 19:21:56 -04:00
if ( rule - > cond ) {
prune_acl_cond ( rule - > cond ) ;
free ( rule - > cond ) ;
2016-11-24 17:57:54 -05:00
free ( rule - > file ) ;
2014-04-22 19:21:56 -04:00
}
2008-05-31 07:53:23 -04:00
free ( rule ) ;
}
2008-06-07 17:08:56 -04:00
list_for_each_entry_safe ( rdr , rdrb , & p - > redirect_rules , list ) {
LIST_DEL ( & rdr - > list ) ;
2010-01-03 14:03:03 -05:00
if ( rdr - > cond ) {
prune_acl_cond ( rdr - > cond ) ;
free ( rdr - > cond ) ;
}
2008-06-07 17:08:56 -04:00
free ( rdr - > rdr_str ) ;
2013-11-29 06:15:45 -05:00
list_for_each_entry_safe ( lf , lfb , & rdr - > rdr_fmt , list ) {
LIST_DEL ( & lf - > list ) ;
free ( lf ) ;
}
2008-06-07 17:08:56 -04:00
free ( rdr ) ;
}
2011-10-12 11:50:54 -04:00
list_for_each_entry_safe ( log , logb , & p - > logsrvs , list ) {
LIST_DEL ( & log - > list ) ;
free ( log ) ;
}
2012-02-08 10:37:49 -05:00
list_for_each_entry_safe ( lf , lfb , & p - > logformat , list ) {
LIST_DEL ( & lf - > list ) ;
free ( lf ) ;
}
2015-09-25 13:17:44 -04:00
list_for_each_entry_safe ( lf , lfb , & p - > logformat_sd , list ) {
LIST_DEL ( & lf - > list ) ;
free ( lf ) ;
}
2011-07-15 00:14:09 -04:00
deinit_tcp_rules ( & p - > tcp_req . inspect_rules ) ;
deinit_tcp_rules ( & p - > tcp_req . l4_rules ) ;
2011-07-15 00:14:11 -04:00
deinit_stick_rules ( & p - > storersp_rules ) ;
deinit_stick_rules ( & p - > sticking_rules ) ;
2006-06-25 20:48:02 -04:00
h = p - > req_cap ;
while ( h ) {
h_next = h - > next ;
2008-08-03 06:19:50 -04:00
free ( h - > name ) ;
2017-11-24 11:34:44 -05:00
pool_destroy ( h - > pool ) ;
2006-06-25 20:48:02 -04:00
free ( h ) ;
h = h_next ;
} /* end while(h) */
h = p - > rsp_cap ;
while ( h ) {
h_next = h - > next ;
2008-08-03 06:19:50 -04:00
free ( h - > name ) ;
2017-11-24 11:34:44 -05:00
pool_destroy ( h - > pool ) ;
2006-06-25 20:48:02 -04:00
free ( h ) ;
h = h_next ;
} /* end while(h) */
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
2006-06-25 20:48:02 -04:00
s = p - > srv ;
while ( s ) {
s_next = s - > next ;
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
2012-09-28 09:01:02 -04:00
if ( s - > check . task ) {
task_delete ( s - > check . task ) ;
task_free ( s - > check . task ) ;
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
}
2013-11-24 20:46:36 -05:00
if ( s - > agent . task ) {
task_delete ( s - > agent . task ) ;
task_free ( s - > agent . task ) ;
}
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
2011-10-31 06:53:20 -04:00
if ( s - > warmup ) {
task_delete ( s - > warmup ) ;
task_free ( s - > warmup ) ;
}
2008-08-03 06:19:50 -04:00
free ( s - > id ) ;
free ( s - > cookie ) ;
2018-07-10 11:43:27 -04:00
free ( s - > check . bi . area ) ;
free ( s - > check . bo . area ) ;
free ( s - > agent . bi . area ) ;
free ( s - > agent . bo . area ) ;
2015-10-21 21:19:05 -04:00
free ( s - > agent . send_string ) ;
MAJOR: dns: Refactor the DNS code
This is a huge patch with many changes, all about the DNS. Initially, the idea
was to update the DNS part to ease the threads support integration. But quickly,
I started to refactor some parts. And after several iterations, it was
impossible for me to commit the different parts atomically. So, instead of
adding tens of patches, often reworking the same parts, it was easier to merge
all my changes in a uniq patch. Here are all changes made on the DNS.
First, the DNS initialization has been refactored. The DNS configuration parsing
remains untouched, in cfgparse.c. But all checks have been moved in a post-check
callback. In the function dns_finalize_config, for each resolvers, the
nameservers configuration is tested and the task used to manage DNS resolutions
is created. The links between the backend's servers and the resolvers are also
created at this step. Here no connection are kept alive. So there is no needs
anymore to reopen them after HAProxy fork. Connections used to send DNS queries
will be opened on demand.
Then, the way DNS requesters are linked to a DNS resolution has been
reworked. The resolution used by a requester is now referenced into the
dns_requester structure and the resolution pointers in server and dns_srvrq
structures have been removed. wait and curr list of requesters, for a DNS
resolution, have been replaced by a uniq list. And Finally, the way a requester
is removed from a DNS resolution has been simplified. Now everything is done in
dns_unlink_resolution.
srv_set_fqdn function has been simplified. Now, there is only 1 way to set the
server's FQDN, independently it is done by the CLI or when a SRV record is
resolved.
The static DNS resolutions pool has been replaced by a dynamoc pool. The part
has been modified by Baptiste Assmann.
The way the DNS resolutions are triggered by the task or by a health-check has
been totally refactored. Now, all timeouts are respected. Especially
hold.valid. The default frequency to wake up a resolvers is now configurable
using "timeout resolve" parameter.
Now, as documented, as long as invalid repsonses are received, we really wait
all name servers responses before retrying.
As far as possible, resources allocated during DNS configuration parsing are
releases when HAProxy is shutdown.
Beside all these changes, the code has been cleaned to ease code review and the
doc has been updated.
2017-09-27 05:00:59 -04:00
free ( s - > hostname_dn ) ;
2014-09-05 04:08:23 -04:00
free ( ( char * ) s - > conf . file ) ;
2018-11-22 12:50:54 -05:00
free ( s - > idle_conns ) ;
free ( s - > priv_conns ) ;
free ( s - > safe_conns ) ;
2018-12-02 08:11:41 -05:00
free ( s - > idle_orphan_conns ) ;
if ( s - > idle_task ) {
int i ;
for ( i = 0 ; i < global . nbthread ; i + + )
task_free ( s - > idle_task [ i ] ) ;
free ( s - > idle_task ) ;
}
2016-12-22 15:16:08 -05:00
if ( s - > use_ssl | | s - > check . use_ssl ) {
if ( xprt_get ( XPRT_SSL ) & & xprt_get ( XPRT_SSL ) - > destroy_srv )
xprt_get ( XPRT_SSL ) - > destroy_srv ( s ) ;
}
2017-11-07 04:42:54 -05:00
HA_SPIN_DESTROY ( & s - > lock ) ;
2006-06-25 20:48:02 -04:00
free ( s ) ;
s = s_next ;
} /* end while(s) */
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
2012-09-20 10:48:07 -04:00
list_for_each_entry_safe ( l , l_next , & p - > conf . listeners , by_fe ) {
2017-04-05 19:05:05 -04:00
/*
* Zombie proxy , the listener just pretend to be up
* because they still hold an opened fd .
* Close it and give the listener its real state .
*/
if ( p - > state = = PR_STSTOPPED & & l - > state > = LI_ZOMBIE ) {
close ( l - > fd ) ;
l - > state = LI_INIT ;
}
2010-09-03 04:38:17 -04:00
unbind_listener ( l ) ;
delete_listener ( l ) ;
2012-09-20 10:48:07 -04:00
LIST_DEL ( & l - > by_fe ) ;
LIST_DEL ( & l - > by_bind ) ;
2010-02-05 14:31:44 -05:00
free ( l - > name ) ;
free ( l - > counters ) ;
2006-06-25 20:48:02 -04:00
free ( l ) ;
2012-09-20 10:48:07 -04:00
}
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
2012-09-20 10:48:07 -04:00
/* Release unused SSL configs. */
2012-09-13 11:54:29 -04:00
list_for_each_entry_safe ( bind_conf , bind_back , & p - > conf . bind , by_fe ) {
2016-12-22 11:30:54 -05:00
if ( bind_conf - > xprt - > destroy_bind_conf )
bind_conf - > xprt - > destroy_bind_conf ( bind_conf ) ;
2012-09-13 11:54:29 -04:00
free ( bind_conf - > file ) ;
free ( bind_conf - > arg ) ;
LIST_DEL ( & bind_conf - > by_fe ) ;
free ( bind_conf ) ;
}
2012-09-07 10:58:00 -04:00
MAJOR: filters: Add filters support
This patch adds the support of filters in HAProxy. The main idea is to have a
way to "easely" extend HAProxy by adding some "modules", called filters, that
will be able to change HAProxy behavior in a programmatic way.
To do so, many entry points has been added in code to let filters to hook up to
different steps of the processing. A filter must define a flt_ops sutrctures
(see include/types/filters.h for details). This structure contains all available
callbacks that a filter can define:
struct flt_ops {
/*
* Callbacks to manage the filter lifecycle
*/
int (*init) (struct proxy *p);
void (*deinit)(struct proxy *p);
int (*check) (struct proxy *p);
/*
* Stream callbacks
*/
void (*stream_start) (struct stream *s);
void (*stream_accept) (struct stream *s);
void (*session_establish)(struct stream *s);
void (*stream_stop) (struct stream *s);
/*
* HTTP callbacks
*/
int (*http_start) (struct stream *s, struct http_msg *msg);
int (*http_start_body) (struct stream *s, struct http_msg *msg);
int (*http_start_chunk) (struct stream *s, struct http_msg *msg);
int (*http_data) (struct stream *s, struct http_msg *msg);
int (*http_last_chunk) (struct stream *s, struct http_msg *msg);
int (*http_end_chunk) (struct stream *s, struct http_msg *msg);
int (*http_chunk_trailers)(struct stream *s, struct http_msg *msg);
int (*http_end_body) (struct stream *s, struct http_msg *msg);
void (*http_end) (struct stream *s, struct http_msg *msg);
void (*http_reset) (struct stream *s, struct http_msg *msg);
int (*http_pre_process) (struct stream *s, struct http_msg *msg);
int (*http_post_process) (struct stream *s, struct http_msg *msg);
void (*http_reply) (struct stream *s, short status,
const struct chunk *msg);
};
To declare and use a filter, in the configuration, the "filter" keyword must be
used in a listener/frontend section:
frontend test
...
filter <FILTER-NAME> [OPTIONS...]
The filter referenced by the <FILTER-NAME> must declare a configuration parser
on its own name to fill flt_ops and filter_conf field in the proxy's
structure. An exemple will be provided later to make it perfectly clear.
For now, filters cannot be used in backend section. But this is only a matter of
time. Documentation will also be added later. This is the first commit of a long
list about filters.
It is possible to have several filters on the same listener/frontend. These
filters are stored in an array of at most MAX_FILTERS elements (define in
include/types/filters.h). Again, this will be replaced later by a list of
filters.
The filter API has been highly refactored. Main changes are:
* Now, HA supports an infinite number of filters per proxy. To do so, filters
are stored in list.
* Because filters are stored in list, filters state has been moved from the
channel structure to the filter structure. This is cleaner because there is no
more info about filters in channel structure.
* It is possible to defined filters on backends only. For such filters,
stream_start/stream_stop callbacks are not called. Of course, it is possible
to mix frontend and backend filters.
* Now, TCP streams are also filtered. All callbacks without the 'http_' prefix
are called for all kind of streams. In addition, 2 new callbacks were added to
filter data exchanged through a TCP stream:
- tcp_data: it is called when new data are available or when old unprocessed
data are still waiting.
- tcp_forward_data: it is called when some data can be consumed.
* New callbacks attached to channel were added:
- channel_start_analyze: it is called when a filter is ready to process data
exchanged through a channel. 2 new analyzers (a frontend and a backend)
are attached to channels to call this callback. For a frontend filter, it
is called before any other analyzer. For a backend filter, it is called
when a backend is attached to a stream. So some processing cannot be
filtered in that case.
- channel_analyze: it is called before each analyzer attached to a channel,
expects analyzers responsible for data sending.
- channel_end_analyze: it is called when all other analyzers have finished
their processing. A new analyzers is attached to channels to call this
callback. For a TCP stream, this is always the last one called. For a HTTP
one, the callback is called when a request/response ends, so it is called
one time for each request/response.
* 'session_established' callback has been removed. Everything that is done in
this callback can be handled by 'channel_start_analyze' on the response
channel.
* 'http_pre_process' and 'http_post_process' callbacks have been replaced by
'channel_analyze'.
* 'http_start' callback has been replaced by 'http_headers'. This new one is
called just before headers sending and parsing of the body.
* 'http_end' callback has been replaced by 'channel_end_analyze'.
* It is possible to set a forwarder for TCP channels. It was already possible to
do it for HTTP ones.
* Forwarders can partially consumed forwardable data. For this reason a new
HTTP message state was added before HTTP_MSG_DONE : HTTP_MSG_ENDING.
Now all filters can define corresponding callbacks (http_forward_data
and tcp_forward_data). Each filter owns 2 offsets relative to buf->p, next and
forward, to track, respectively, input data already parsed but not forwarded yet
by the filter and parsed data considered as forwarded by the filter. A any time,
we have the warranty that a filter cannot parse or forward more input than
previous ones. And, of course, it cannot forward more input than it has
parsed. 2 macros has been added to retrieve these offets: FLT_NXT and FLT_FWD.
In addition, 2 functions has been added to change the 'next size' and the
'forward size' of a filter. When a filter parses input data, it can alter these
data, so the size of these data can vary. This action has an effet on all
previous filters that must be handled. To do so, the function
'filter_change_next_size' must be called, passing the size variation. In the
same spirit, if a filter alter forwarded data, it must call the function
'filter_change_forward_size'. 'filter_change_next_size' can be called in
'http_data' and 'tcp_data' callbacks and only these ones. And
'filter_change_forward_size' can be called in 'http_forward_data' and
'tcp_forward_data' callbacks and only these ones. The data changes are the
filter responsability, but with some limitation. It must not change already
parsed/forwarded data or data that previous filters have not parsed/forwarded
yet.
Because filters can be used on backends, when we the backend is set for a
stream, we add filters defined for this backend in the filter list of the
stream. But we must only do that when the backend and the frontend of the stream
are not the same. Else same filters are added a second time leading to undefined
behavior.
The HTTP compression code had to be moved.
So it simplifies http_response_forward_body function. To do so, the way the data
are forwarded has changed. Now, a filter (and only one) can forward data. In a
commit to come, this limitation will be removed to let all filters take part to
data forwarding. There are 2 new functions that filters should use to deal with
this feature:
* flt_set_http_data_forwarder: This function sets the filter (using its id)
that will forward data for the specified HTTP message. It is possible if it
was not already set by another filter _AND_ if no data was yet forwarded
(msg->msg_state <= HTTP_MSG_BODY). It returns -1 if an error occurs.
* flt_http_data_forwarder: This function returns the filter id that will
forward data for the specified HTTP message. If there is no forwarder set, it
returns -1.
When an HTTP data forwarder is set for the response, the HTTP compression is
disabled. Of course, this is not definitive.
2015-04-30 05:48:27 -04:00
flt_deinit ( p ) ;
2010-02-05 14:31:44 -05:00
free ( p - > desc ) ;
free ( p - > fwdfor_hdr_name ) ;
2011-01-06 11:51:27 -05:00
free_http_req_rules ( & p - > http_req_rules ) ;
2014-06-16 14:05:59 -04:00
free_http_res_rules ( & p - > http_res_rules ) ;
2017-11-22 10:53:53 -05:00
task_free ( p - > task ) ;
2010-01-29 11:58:21 -05:00
2017-11-24 11:34:44 -05:00
pool_destroy ( p - > req_cap_pool ) ;
pool_destroy ( p - > rsp_cap_pool ) ;
pool_destroy ( p - > table . pool ) ;
2010-01-29 11:50:44 -05:00
2007-05-13 18:39:29 -04:00
p0 = p ;
2006-06-25 20:48:02 -04:00
p = p - > next ;
2017-11-07 04:42:54 -05:00
HA_SPIN_DESTROY ( & p0 - > lbprm . lock ) ;
HA_SPIN_DESTROY ( & p0 - > lock ) ;
2007-05-13 18:39:29 -04:00
free ( p0 ) ;
2006-06-25 20:48:02 -04:00
} /* end while(p) */
2007-10-16 06:25:14 -04:00
2008-05-31 07:53:23 -04:00
while ( ua ) {
uap = ua ;
ua = ua - > next ;
2008-08-03 06:19:50 -04:00
free ( uap - > uri_prefix ) ;
free ( uap - > auth_realm ) ;
2009-10-02 16:51:14 -04:00
free ( uap - > node ) ;
free ( uap - > desc ) ;
2008-05-31 07:53:23 -04:00
2010-01-29 13:29:32 -05:00
userlist_free ( uap - > userlist ) ;
2011-01-06 11:51:27 -05:00
free_http_req_rules ( & uap - > http_req_rules ) ;
2010-01-29 13:29:32 -05:00
2008-05-31 07:53:23 -04:00
free ( uap ) ;
}
2010-01-29 11:50:44 -05:00
userlist_free ( userlist ) ;
2015-09-25 07:02:25 -04:00
cfg_unregister_sections ( ) ;
2017-07-26 09:33:35 -04:00
deinit_log_buffers ( ) ;
2015-09-25 07:02:25 -04:00
2007-10-16 06:25:14 -04:00
protocol_unbind_all ( ) ;
2016-12-21 14:46:26 -05:00
list_for_each_entry ( pdf , & post_deinit_list , list )
pdf - > fct ( ) ;
2010-12-29 11:05:48 -05:00
free ( global . log_send_hostname ) ; global . log_send_hostname = NULL ;
2015-10-01 07:18:13 -04:00
chunk_destroy ( & global . log_tag ) ;
2008-08-03 06:19:50 -04:00
free ( global . chroot ) ; global . chroot = NULL ;
free ( global . pidfile ) ; global . pidfile = NULL ;
2009-10-02 16:51:14 -04:00
free ( global . node ) ; global . node = NULL ;
free ( global . desc ) ; global . desc = NULL ;
2008-08-03 06:19:50 -04:00
free ( oldpids ) ; oldpids = NULL ;
2017-11-22 10:53:53 -05:00
task_free ( global_listener_queue_task ) ; global_listener_queue_task = NULL ;
2008-05-31 07:53:23 -04:00
2011-10-12 11:50:54 -04:00
list_for_each_entry_safe ( log , logb , & global . logsrvs , list ) {
LIST_DEL ( & log - > list ) ;
free ( log ) ;
}
2010-01-03 15:12:30 -05:00
list_for_each_entry_safe ( wl , wlb , & cfg_cfgfiles , list ) {
2016-05-13 17:52:55 -04:00
free ( wl - > s ) ;
2010-01-03 15:12:30 -05:00
LIST_DEL ( & wl - > list ) ;
free ( wl ) ;
}
2016-12-21 12:43:10 -05:00
list_for_each_entry_safe ( bol , bolb , & build_opts_list , list ) {
if ( bol - > must_free )
free ( ( void * ) bol - > str ) ;
LIST_DEL ( & bol - > list ) ;
free ( bol ) ;
}
2016-11-09 05:36:17 -05:00
vars_prune ( & global . vars , NULL , NULL ) ;
2018-11-26 09:57:34 -05:00
pool_destroy_all ( ) ;
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
deinit_pollers ( ) ;
2006-06-25 20:48:02 -04:00
} /* end deinit() */
2018-11-06 11:37:16 -05:00
/* This is a wrapper for the sockpair FD, It tests if the socket received an
* EOF , if not , it calls listener_accept */
void mworker_accept_wrapper ( int fd )
MAJOR: threads/fd: Make fd stuffs thread-safe
Many changes have been made to do so. First, the fd_updt array, where all
pending FDs for polling are stored, is now a thread-local array. Then 3 locks
have been added to protect, respectively, the fdtab array, the fd_cache array
and poll information. In addition, a lock for each entry in the fdtab array has
been added to protect all accesses to a specific FD or its information.
For pollers, according to the poller, the way to manage the concurrency is
different. There is a poller loop on each thread. So the set of monitored FDs
may need to be protected. epoll and kqueue are thread-safe per-se, so there few
things to do to protect these pollers. This is not possible with select and
poll, so there is no sharing between the threads. The poller on each thread is
independant from others.
Finally, per-thread init/deinit functions are used for each pollers and for FD
part for manage thread-local ressources.
Now, you must be carefull when a FD is created during the HAProxy startup. All
update on the FD state must be made in the threads context and never before
their creation. This is mandatory because fd_updt array is thread-local and
initialized only for threads. Because there is no pollers for the main one, this
array remains uninitialized in this context. For this reason, listeners are now
enabled in run_thread_poll_loop function, just like the worker pipe.
2017-05-29 04:40:41 -04:00
{
char c ;
2018-11-06 11:37:16 -05:00
int ret ;
MAJOR: threads/fd: Make fd stuffs thread-safe
Many changes have been made to do so. First, the fd_updt array, where all
pending FDs for polling are stored, is now a thread-local array. Then 3 locks
have been added to protect, respectively, the fdtab array, the fd_cache array
and poll information. In addition, a lock for each entry in the fdtab array has
been added to protect all accesses to a specific FD or its information.
For pollers, according to the poller, the way to manage the concurrency is
different. There is a poller loop on each thread. So the set of monitored FDs
may need to be protected. epoll and kqueue are thread-safe per-se, so there few
things to do to protect these pollers. This is not possible with select and
poll, so there is no sharing between the threads. The poller on each thread is
independant from others.
Finally, per-thread init/deinit functions are used for each pollers and for FD
part for manage thread-local ressources.
Now, you must be carefull when a FD is created during the HAProxy startup. All
update on the FD state must be made in the threads context and never before
their creation. This is mandatory because fd_updt array is thread-local and
initialized only for threads. Because there is no pollers for the main one, this
array remains uninitialized in this context. For this reason, listeners are now
enabled in run_thread_poll_loop function, just like the worker pipe.
2017-05-29 04:40:41 -04:00
2018-11-06 11:37:16 -05:00
while ( 1 ) {
ret = recv ( fd , & c , 1 , MSG_PEEK ) ;
if ( ret = = - 1 ) {
if ( errno = = EINTR )
continue ;
if ( errno = = EAGAIN ) {
fd_cant_recv ( fd ) ;
return ;
}
break ;
} else if ( ret > 0 ) {
listener_accept ( fd ) ;
MAJOR: threads/fd: Make fd stuffs thread-safe
Many changes have been made to do so. First, the fd_updt array, where all
pending FDs for polling are stored, is now a thread-local array. Then 3 locks
have been added to protect, respectively, the fdtab array, the fd_cache array
and poll information. In addition, a lock for each entry in the fdtab array has
been added to protect all accesses to a specific FD or its information.
For pollers, according to the poller, the way to manage the concurrency is
different. There is a poller loop on each thread. So the set of monitored FDs
may need to be protected. epoll and kqueue are thread-safe per-se, so there few
things to do to protect these pollers. This is not possible with select and
poll, so there is no sharing between the threads. The poller on each thread is
independant from others.
Finally, per-thread init/deinit functions are used for each pollers and for FD
part for manage thread-local ressources.
Now, you must be carefull when a FD is created during the HAProxy startup. All
update on the FD state must be made in the threads context and never before
their creation. This is mandatory because fd_updt array is thread-local and
initialized only for threads. Because there is no pollers for the main one, this
array remains uninitialized in this context. For this reason, listeners are now
enabled in run_thread_poll_loop function, just like the worker pipe.
2017-05-29 04:40:41 -04:00
return ;
2018-11-06 11:37:16 -05:00
} else if ( ret = = 0 ) {
/* At this step the master is down before
* this worker perform a ' normal ' exit .
* So we want to exit with an error but
* other threads could currently process
* some stuff so we can ' t perform a clean
* deinit ( ) .
*/
exit ( EXIT_FAILURE ) ;
MAJOR: threads/fd: Make fd stuffs thread-safe
Many changes have been made to do so. First, the fd_updt array, where all
pending FDs for polling are stored, is now a thread-local array. Then 3 locks
have been added to protect, respectively, the fdtab array, the fd_cache array
and poll information. In addition, a lock for each entry in the fdtab array has
been added to protect all accesses to a specific FD or its information.
For pollers, according to the poller, the way to manage the concurrency is
different. There is a poller loop on each thread. So the set of monitored FDs
may need to be protected. epoll and kqueue are thread-safe per-se, so there few
things to do to protect these pollers. This is not possible with select and
poll, so there is no sharing between the threads. The poller on each thread is
independant from others.
Finally, per-thread init/deinit functions are used for each pollers and for FD
part for manage thread-local ressources.
Now, you must be carefull when a FD is created during the HAProxy startup. All
update on the FD state must be made in the threads context and never before
their creation. This is mandatory because fd_updt array is thread-local and
initialized only for threads. Because there is no pollers for the main one, this
array remains uninitialized in this context. For this reason, listeners are now
enabled in run_thread_poll_loop function, just like the worker pipe.
2017-05-29 04:40:41 -04:00
}
}
return ;
}
2018-11-06 11:37:16 -05:00
/*
* This function register the accept wrapper for the sockpair of the master worker
*/
BUG/MEDIUM: threads/mworker: fix a race on startup
Marc Fournier reported an interesting case when using threads with the
master-worker mode : sometimes, a listener would have its FD closed
during startup. Sometimes it could even be health checks seeing this.
What happens is that after the threads are created, and the pollers
enabled on each threads, the master-worker pipe is registered, and at
the same time a close() is performed on the write side of this pipe
since the children must not use it.
But since this is replicated in every thread, what happens is that the
first thread closes the pipe, thus releases the FD, and the next thread
starting a listener in parallel gets this FD reassigned. Then another
thread closes the FD again, which this time corresponds to the listener.
It can also happen with the health check sockets if they're started
early enough.
This patch splits the mworker_pipe_register() function in two, so that
the close() of the write side of the FD is performed very early after the
fork() and long before threads are created (we don't need to delay it
anyway). Only the pipe registration is done in the threaded code since
it is important that the pollers are properly allocated for this.
The mworker_pipe_register() function now takes care of registering the
pipe only once, and this is guaranteed by a new surrounding lock.
The call to protocol_enable_all() looks fragile in theory since it
scans the list of proxies and their listeners, though in practice
all threads scan the same list and take the same locks for each
listener so it's not possible that any of them escapes the process
and finishes before all listeners are started. And the operation is
idempotent.
This fix must be backported to 1.8. Thanks to Marc for providing very
detailed traces clearly showing the problem.
2018-01-23 13:01:49 -05:00
void mworker_pipe_register ( )
MAJOR: threads/fd: Make fd stuffs thread-safe
Many changes have been made to do so. First, the fd_updt array, where all
pending FDs for polling are stored, is now a thread-local array. Then 3 locks
have been added to protect, respectively, the fdtab array, the fd_cache array
and poll information. In addition, a lock for each entry in the fdtab array has
been added to protect all accesses to a specific FD or its information.
For pollers, according to the poller, the way to manage the concurrency is
different. There is a poller loop on each thread. So the set of monitored FDs
may need to be protected. epoll and kqueue are thread-safe per-se, so there few
things to do to protect these pollers. This is not possible with select and
poll, so there is no sharing between the threads. The poller on each thread is
independant from others.
Finally, per-thread init/deinit functions are used for each pollers and for FD
part for manage thread-local ressources.
Now, you must be carefull when a FD is created during the HAProxy startup. All
update on the FD state must be made in the threads context and never before
their creation. This is mandatory because fd_updt array is thread-local and
initialized only for threads. Because there is no pollers for the main one, this
array remains uninitialized in this context. For this reason, listeners are now
enabled in run_thread_poll_loop function, just like the worker pipe.
2017-05-29 04:40:41 -04:00
{
2018-11-06 11:37:16 -05:00
/* The iocb should be already initialized with listener_accept */
2018-11-07 02:38:32 -05:00
if ( fdtab [ proc_self - > ipc_fd [ 1 ] ] . iocb = = mworker_accept_wrapper )
return ;
2006-06-25 20:48:02 -04:00
2018-09-11 04:06:26 -04:00
fcntl ( proc_self - > ipc_fd [ 1 ] , F_SETFL , O_NONBLOCK ) ;
2018-10-11 09:27:07 -04:00
/* In multi-tread, we need only one thread to process
* events on the pipe with master
*/
2018-11-08 06:00:14 -05:00
fd_insert ( proc_self - > ipc_fd [ 1 ] , fdtab [ proc_self - > ipc_fd [ 1 ] ] . owner , mworker_accept_wrapper , 1 ) ;
2018-09-11 04:06:26 -04:00
fd_want_recv ( proc_self - > ipc_fd [ 1 ] ) ;
MAJOR: threads/fd: Make fd stuffs thread-safe
Many changes have been made to do so. First, the fd_updt array, where all
pending FDs for polling are stored, is now a thread-local array. Then 3 locks
have been added to protect, respectively, the fdtab array, the fd_cache array
and poll information. In addition, a lock for each entry in the fdtab array has
been added to protect all accesses to a specific FD or its information.
For pollers, according to the poller, the way to manage the concurrency is
different. There is a poller loop on each thread. So the set of monitored FDs
may need to be protected. epoll and kqueue are thread-safe per-se, so there few
things to do to protect these pollers. This is not possible with select and
poll, so there is no sharing between the threads. The poller on each thread is
independant from others.
Finally, per-thread init/deinit functions are used for each pollers and for FD
part for manage thread-local ressources.
Now, you must be carefull when a FD is created during the HAProxy startup. All
update on the FD state must be made in the threads context and never before
their creation. This is mandatory because fd_updt array is thread-local and
initialized only for threads. Because there is no pollers for the main one, this
array remains uninitialized in this context. For this reason, listeners are now
enabled in run_thread_poll_loop function, just like the worker pipe.
2017-05-29 04:40:41 -04:00
}
2017-10-19 05:59:44 -04:00
2011-07-25 10:33:49 -04:00
/* Runs the polling loop */
2016-12-21 12:19:57 -05:00
static void run_poll_loop ( )
2007-04-08 10:39:58 -04:00
{
2018-01-20 13:30:13 -05:00
int next , exp ;
2007-04-08 10:39:58 -04:00
2008-06-23 08:00:57 -04:00
tv_update_date ( 0 , 1 ) ;
2007-04-08 10:39:58 -04:00
while ( 1 ) {
2014-12-15 07:26:01 -05:00
/* Process a few tasks */
process_runnable_tasks ( ) ;
2018-06-07 03:46:01 -04:00
/* check if we caught some signals and process them in the
first thread */
if ( tid = = 0 )
signal_process_queue ( ) ;
2009-05-10 03:01:21 -04:00
2008-06-29 16:40:23 -04:00
/* Check if we can expire some tasks */
2014-12-15 07:26:01 -05:00
next = wake_expired_tasks ( ) ;
2007-04-08 10:39:58 -04:00
2018-08-02 04:54:31 -04:00
/* stop when there's nothing left to do */
2018-11-16 10:57:20 -05:00
if ( ( jobs - unstoppable_jobs ) = = 0 )
2018-08-02 04:54:31 -04:00
break ;
2007-04-08 10:39:58 -04:00
2015-04-13 14:44:19 -04:00
/* expire immediately if events are pending */
2018-01-20 13:30:13 -05:00
exp = now_ms ;
2018-01-15 06:16:34 -05:00
if ( fd_cache_mask & tid_bit )
2018-01-20 13:30:13 -05:00
activity [ tid ] . wake_cache + + ;
else if ( active_tasks_mask & tid_bit )
activity [ tid ] . wake_tasks + + ;
2018-06-07 03:46:01 -04:00
else if ( signal_queue_len & & tid = = 0 )
2018-01-20 13:30:13 -05:00
activity [ tid ] . wake_signal + + ;
2018-07-26 11:55:11 -04:00
else {
HA_ATOMIC_OR ( & sleeping_thread_mask , tid_bit ) ;
__ha_barrier_store ( ) ;
if ( active_tasks_mask & tid_bit ) {
activity [ tid ] . wake_tasks + + ;
HA_ATOMIC_AND ( & sleeping_thread_mask , ~ tid_bit ) ;
} else
exp = next ;
}
2015-04-13 14:44:19 -04:00
2008-06-29 16:40:23 -04:00
/* The poller will ensure it returns around <next> */
2018-01-20 13:30:13 -05:00
cur_poller . poll ( & cur_poller , exp ) ;
2018-07-26 11:55:11 -04:00
if ( sleeping_thread_mask & tid_bit )
HA_ATOMIC_AND ( & sleeping_thread_mask , ~ tid_bit ) ;
2014-01-25 13:24:15 -05:00
fd_process_cached_events ( ) ;
2017-10-03 08:46:45 -04:00
2018-01-20 13:30:13 -05:00
activity [ tid ] . loops + + ;
2007-04-08 10:39:58 -04:00
}
}
MAJOR: threads: Start threads to experiment multithreading
[WARNING] For now, HAProxy is not thread-safe, so from this commit, it will be
broken for a while, when compiled with threads.
When nbthread parameter is greater than 1, HAProxy will create the corresponding
number of threads. If nbthread is set to 1, nothing should be done. So if there
are concurrency issues (and be sure there will be, unfortunatly), an obvious
workaround is to disable the multithreading...
Each created threads will run a polling loop. So, in a certain way, it is pretty
similar to the nbproc mode ("outside" the bugs and the lock
contention). Nevertheless, there are an init and a deinit steps for each thread
to deal with per-thread allocation.
Each thread has a tid (thread-id), numbered from 0 to (nbtread-1). It is used in
many place to do bitwise operations or to improve debugging information.
2017-08-29 09:38:48 -04:00
static void * run_thread_poll_loop ( void * data )
{
struct per_thread_init_fct * ptif ;
struct per_thread_deinit_fct * ptdf ;
2018-01-25 10:10:16 -05:00
__decl_hathreads ( static HA_SPINLOCK_T start_lock ) ;
MAJOR: threads: Start threads to experiment multithreading
[WARNING] For now, HAProxy is not thread-safe, so from this commit, it will be
broken for a while, when compiled with threads.
When nbthread parameter is greater than 1, HAProxy will create the corresponding
number of threads. If nbthread is set to 1, nothing should be done. So if there
are concurrency issues (and be sure there will be, unfortunatly), an obvious
workaround is to disable the multithreading...
Each created threads will run a polling loop. So, in a certain way, it is pretty
similar to the nbproc mode ("outside" the bugs and the lock
contention). Nevertheless, there are an init and a deinit steps for each thread
to deal with per-thread allocation.
Each thread has a tid (thread-id), numbered from 0 to (nbtread-1). It is used in
many place to do bitwise operations or to improve debugging information.
2017-08-29 09:38:48 -04:00
2018-08-01 13:12:20 -04:00
ha_set_tid ( * ( ( unsigned int * ) data ) ) ;
MAJOR: threads: Start threads to experiment multithreading
[WARNING] For now, HAProxy is not thread-safe, so from this commit, it will be
broken for a while, when compiled with threads.
When nbthread parameter is greater than 1, HAProxy will create the corresponding
number of threads. If nbthread is set to 1, nothing should be done. So if there
are concurrency issues (and be sure there will be, unfortunatly), an obvious
workaround is to disable the multithreading...
Each created threads will run a polling loop. So, in a certain way, it is pretty
similar to the nbproc mode ("outside" the bugs and the lock
contention). Nevertheless, there are an init and a deinit steps for each thread
to deal with per-thread allocation.
Each thread has a tid (thread-id), numbered from 0 to (nbtread-1). It is used in
many place to do bitwise operations or to improve debugging information.
2017-08-29 09:38:48 -04:00
tv_update_date ( - 1 , - 1 ) ;
list_for_each_entry ( ptif , & per_thread_init_list , list ) {
if ( ! ptif - > fct ( ) ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " failed to initialize thread %u. \n " , tid ) ;
MAJOR: threads: Start threads to experiment multithreading
[WARNING] For now, HAProxy is not thread-safe, so from this commit, it will be
broken for a while, when compiled with threads.
When nbthread parameter is greater than 1, HAProxy will create the corresponding
number of threads. If nbthread is set to 1, nothing should be done. So if there
are concurrency issues (and be sure there will be, unfortunatly), an obvious
workaround is to disable the multithreading...
Each created threads will run a polling loop. So, in a certain way, it is pretty
similar to the nbproc mode ("outside" the bugs and the lock
contention). Nevertheless, there are an init and a deinit steps for each thread
to deal with per-thread allocation.
Each thread has a tid (thread-id), numbered from 0 to (nbtread-1). It is used in
many place to do bitwise operations or to improve debugging information.
2017-08-29 09:38:48 -04:00
exit ( 1 ) ;
}
}
2018-09-11 04:06:26 -04:00
if ( ( global . mode & MODE_MWORKER ) & & master = = 0 ) {
BUG/MEDIUM: threads/mworker: fix a race on startup
Marc Fournier reported an interesting case when using threads with the
master-worker mode : sometimes, a listener would have its FD closed
during startup. Sometimes it could even be health checks seeing this.
What happens is that after the threads are created, and the pollers
enabled on each threads, the master-worker pipe is registered, and at
the same time a close() is performed on the write side of this pipe
since the children must not use it.
But since this is replicated in every thread, what happens is that the
first thread closes the pipe, thus releases the FD, and the next thread
starting a listener in parallel gets this FD reassigned. Then another
thread closes the FD again, which this time corresponds to the listener.
It can also happen with the health check sockets if they're started
early enough.
This patch splits the mworker_pipe_register() function in two, so that
the close() of the write side of the FD is performed very early after the
fork() and long before threads are created (we don't need to delay it
anyway). Only the pipe registration is done in the threaded code since
it is important that the pollers are properly allocated for this.
The mworker_pipe_register() function now takes care of registering the
pipe only once, and this is guaranteed by a new surrounding lock.
The call to protocol_enable_all() looks fragile in theory since it
scans the list of proxies and their listeners, though in practice
all threads scan the same list and take the same locks for each
listener so it's not possible that any of them escapes the process
and finishes before all listeners are started. And the operation is
idempotent.
This fix must be backported to 1.8. Thanks to Marc for providing very
detailed traces clearly showing the problem.
2018-01-23 13:01:49 -05:00
HA_SPIN_LOCK ( START_LOCK , & start_lock ) ;
mworker_pipe_register ( ) ;
HA_SPIN_UNLOCK ( START_LOCK , & start_lock ) ;
}
MAJOR: threads/fd: Make fd stuffs thread-safe
Many changes have been made to do so. First, the fd_updt array, where all
pending FDs for polling are stored, is now a thread-local array. Then 3 locks
have been added to protect, respectively, the fdtab array, the fd_cache array
and poll information. In addition, a lock for each entry in the fdtab array has
been added to protect all accesses to a specific FD or its information.
For pollers, according to the poller, the way to manage the concurrency is
different. There is a poller loop on each thread. So the set of monitored FDs
may need to be protected. epoll and kqueue are thread-safe per-se, so there few
things to do to protect these pollers. This is not possible with select and
poll, so there is no sharing between the threads. The poller on each thread is
independant from others.
Finally, per-thread init/deinit functions are used for each pollers and for FD
part for manage thread-local ressources.
Now, you must be carefull when a FD is created during the HAProxy startup. All
update on the FD state must be made in the threads context and never before
their creation. This is mandatory because fd_updt array is thread-local and
initialized only for threads. Because there is no pollers for the main one, this
array remains uninitialized in this context. For this reason, listeners are now
enabled in run_thread_poll_loop function, just like the worker pipe.
2017-05-29 04:40:41 -04:00
protocol_enable_all ( ) ;
MAJOR: threads: Start threads to experiment multithreading
[WARNING] For now, HAProxy is not thread-safe, so from this commit, it will be
broken for a while, when compiled with threads.
When nbthread parameter is greater than 1, HAProxy will create the corresponding
number of threads. If nbthread is set to 1, nothing should be done. So if there
are concurrency issues (and be sure there will be, unfortunatly), an obvious
workaround is to disable the multithreading...
Each created threads will run a polling loop. So, in a certain way, it is pretty
similar to the nbproc mode ("outside" the bugs and the lock
contention). Nevertheless, there are an init and a deinit steps for each thread
to deal with per-thread allocation.
Each thread has a tid (thread-id), numbered from 0 to (nbtread-1). It is used in
many place to do bitwise operations or to improve debugging information.
2017-08-29 09:38:48 -04:00
run_poll_loop ( ) ;
list_for_each_entry ( ptdf , & per_thread_deinit_list , list )
ptdf - > fct ( ) ;
2017-10-27 07:53:47 -04:00
# ifdef USE_THREAD
2018-06-24 03:37:03 -04:00
HA_ATOMIC_AND ( & all_threads_mask , ~ tid_bit ) ;
2017-10-27 07:53:47 -04:00
if ( tid > 0 )
pthread_exit ( NULL ) ;
MAJOR: threads: Start threads to experiment multithreading
[WARNING] For now, HAProxy is not thread-safe, so from this commit, it will be
broken for a while, when compiled with threads.
When nbthread parameter is greater than 1, HAProxy will create the corresponding
number of threads. If nbthread is set to 1, nothing should be done. So if there
are concurrency issues (and be sure there will be, unfortunatly), an obvious
workaround is to disable the multithreading...
Each created threads will run a polling loop. So, in a certain way, it is pretty
similar to the nbproc mode ("outside" the bugs and the lock
contention). Nevertheless, there are an init and a deinit steps for each thread
to deal with per-thread allocation.
Each thread has a tid (thread-id), numbered from 0 to (nbtread-1). It is used in
many place to do bitwise operations or to improve debugging information.
2017-08-29 09:38:48 -04:00
# endif
2017-10-27 07:53:47 -04:00
return NULL ;
}
MAJOR: threads: Start threads to experiment multithreading
[WARNING] For now, HAProxy is not thread-safe, so from this commit, it will be
broken for a while, when compiled with threads.
When nbthread parameter is greater than 1, HAProxy will create the corresponding
number of threads. If nbthread is set to 1, nothing should be done. So if there
are concurrency issues (and be sure there will be, unfortunatly), an obvious
workaround is to disable the multithreading...
Each created threads will run a polling loop. So, in a certain way, it is pretty
similar to the nbproc mode ("outside" the bugs and the lock
contention). Nevertheless, there are an init and a deinit steps for each thread
to deal with per-thread allocation.
Each thread has a tid (thread-id), numbered from 0 to (nbtread-1). It is used in
many place to do bitwise operations or to improve debugging information.
2017-08-29 09:38:48 -04:00
2011-08-01 14:57:55 -04:00
/* This is the global management task for listeners. It enables listeners waiting
* for global resources when there are enough free resource , or at least once in
* a while . It is designed to be called as a task .
*/
2018-05-25 08:04:04 -04:00
static struct task * manage_global_listener_queue ( struct task * t , void * context , unsigned short state )
2011-08-01 14:57:55 -04:00
{
int next = TICK_ETERNITY ;
/* queue is empty, nothing to do */
if ( LIST_ISEMPTY ( & global_listener_queue ) )
goto out ;
/* If there are still too many concurrent connections, let's wait for
* some of them to go away . We don ' t need to re - arm the timer because
* each of them will scan the queue anyway .
*/
if ( unlikely ( actconn > = global . maxconn ) )
goto out ;
/* We should periodically try to enable listeners waiting for a global
* resource here , because it is possible , though very unlikely , that
* they have been blocked by a temporary lack of global resource such
* as a file descriptor or memory and that the temporary condition has
* disappeared .
*/
2011-09-07 08:26:33 -04:00
dequeue_all_listeners ( & global_listener_queue ) ;
2011-08-01 14:57:55 -04:00
out :
t - > expire = next ;
task_queue ( t ) ;
return t ;
}
2007-04-08 10:39:58 -04:00
2006-06-25 20:48:02 -04:00
int main ( int argc , char * * argv )
{
int err , retry ;
struct rlimit limit ;
2010-10-22 10:06:11 -04:00
char errmsg [ 100 ] ;
2012-09-05 02:02:48 -04:00
int pidfd = - 1 ;
2006-06-25 20:48:02 -04:00
2018-02-03 09:15:21 -05:00
setvbuf ( stdout , NULL , _IONBF , 0 ) ;
2018-11-25 12:43:29 -05:00
/* process all initcalls in order of potential dependency */
RUN_INITCALLS ( STG_PREPARE ) ;
RUN_INITCALLS ( STG_LOCK ) ;
RUN_INITCALLS ( STG_ALLOC ) ;
RUN_INITCALLS ( STG_POOL ) ;
RUN_INITCALLS ( STG_REGISTER ) ;
RUN_INITCALLS ( STG_INIT ) ;
2010-10-22 10:06:11 -04:00
init ( argc , argv ) ;
2010-08-27 11:56:48 -04:00
signal_register_fct ( SIGQUIT , dump , SIGQUIT ) ;
signal_register_fct ( SIGUSR1 , sig_soft_stop , SIGUSR1 ) ;
signal_register_fct ( SIGHUP , sig_dump_state , SIGHUP ) ;
2017-06-01 11:38:51 -04:00
signal_register_fct ( SIGUSR2 , NULL , 0 ) ;
2006-06-25 20:48:02 -04:00
2010-03-17 13:02:46 -04:00
/* Always catch SIGPIPE even on platforms which define MSG_NOSIGNAL.
* Some recent FreeBSD setups report broken pipes , and MSG_NOSIGNAL
* was defined there , so let ' s stay on the safe side .
2006-06-25 20:48:02 -04:00
*/
2010-08-27 11:56:48 -04:00
signal_register_fct ( SIGPIPE , NULL , 0 ) ;
2006-06-25 20:48:02 -04:00
2011-02-16 05:10:36 -05:00
/* ulimits */
if ( ! global . rlimit_nofile )
global . rlimit_nofile = global . maxsock ;
if ( global . rlimit_nofile ) {
limit . rlim_cur = limit . rlim_max = global . rlimit_nofile ;
if ( setrlimit ( RLIMIT_NOFILE , & limit ) = = - 1 ) {
2016-06-21 05:48:18 -04:00
/* try to set it to the max possible at least */
getrlimit ( RLIMIT_NOFILE , & limit ) ;
2016-06-21 05:51:59 -04:00
limit . rlim_cur = limit . rlim_max ;
if ( setrlimit ( RLIMIT_NOFILE , & limit ) ! = - 1 )
getrlimit ( RLIMIT_NOFILE , & limit ) ;
2017-11-24 10:50:31 -05:00
ha_warning ( " [%s.main()] Cannot raise FD limit to %d, limit is %d. \n " , argv [ 0 ] , global . rlimit_nofile , ( int ) limit . rlim_cur ) ;
2016-06-21 05:48:18 -04:00
global . rlimit_nofile = limit . rlim_cur ;
2011-02-16 05:10:36 -05:00
}
}
if ( global . rlimit_memmax ) {
limit . rlim_cur = limit . rlim_max =
2015-12-14 06:46:07 -05:00
global . rlimit_memmax * 1048576ULL ;
2011-02-16 05:10:36 -05:00
# ifdef RLIMIT_AS
if ( setrlimit ( RLIMIT_AS , & limit ) = = - 1 ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " [%s.main()] Cannot fix MEM limit to %d megs. \n " ,
argv [ 0 ] , global . rlimit_memmax ) ;
2011-02-16 05:10:36 -05:00
}
# else
if ( setrlimit ( RLIMIT_DATA , & limit ) = = - 1 ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " [%s.main()] Cannot fix MEM limit to %d megs. \n " ,
argv [ 0 ] , global . rlimit_memmax ) ;
2011-02-16 05:10:36 -05:00
}
# endif
}
2017-04-05 16:33:04 -04:00
if ( old_unixsocket ) {
2017-06-01 11:38:53 -04:00
if ( strcmp ( " /dev/null " , old_unixsocket ) ! = 0 ) {
if ( get_old_sockets ( old_unixsocket ) ! = 0 ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " Failed to get the sockets from the old process! \n " ) ;
2017-06-01 11:38:53 -04:00
if ( ! ( global . mode & MODE_MWORKER ) )
exit ( 1 ) ;
}
2017-04-05 16:33:04 -04:00
}
}
2017-06-01 11:38:53 -04:00
get_cur_unixsocket ( ) ;
2006-06-25 20:48:02 -04:00
/* We will loop at most 100 times with 10 ms delay each time.
* That ' s at most 1 second . We only send a signal to old pids
* if we cannot grab at least one port .
*/
retry = MAX_START_RETRIES ;
err = ERR_NONE ;
while ( retry > = 0 ) {
struct timeval w ;
err = start_proxies ( retry = = 0 | | nb_oldpids = = 0 ) ;
2007-12-20 17:05:50 -05:00
/* exit the loop on no error or fatal error */
if ( ( err & ( ERR_RETRYABLE | ERR_FATAL ) ) ! = ERR_RETRYABLE )
2006-06-25 20:48:02 -04:00
break ;
2010-08-25 06:58:59 -04:00
if ( nb_oldpids = = 0 | | retry = = 0 )
2006-06-25 20:48:02 -04:00
break ;
/* FIXME-20060514: Solaris and OpenBSD do not support shutdown() on
* listening sockets . So on those platforms , it would be wiser to
* simply send SIGUSR1 , which will not be undoable .
*/
2010-08-25 06:58:59 -04:00
if ( tell_old_pids ( SIGTTOU ) = = 0 ) {
/* no need to wait if we can't contact old pids */
retry = 0 ;
continue ;
}
2006-06-25 20:48:02 -04:00
/* give some time to old processes to stop listening */
w . tv_sec = 0 ;
w . tv_usec = 10 * 1000 ;
select ( 0 , NULL , NULL , NULL , & w ) ;
retry - - ;
}
/* Note: start_proxies() sends an alert when it fails. */
2009-02-04 11:05:23 -05:00
if ( ( err & ~ ERR_WARN ) ! = ERR_NONE ) {
2009-06-09 08:36:00 -04:00
if ( retry ! = MAX_START_RETRIES & & nb_oldpids ) {
protocol_unbind_all ( ) ; /* cleanup everything we can */
2006-06-25 20:48:02 -04:00
tell_old_pids ( SIGTTIN ) ;
2009-06-09 08:36:00 -04:00
}
2006-06-25 20:48:02 -04:00
exit ( 1 ) ;
}
2018-11-21 09:48:31 -05:00
if ( ! ( global . mode & MODE_MWORKER_WAIT ) & & listeners = = 0 ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " [%s.main()] No enabled listener found (check for 'bind' directives) ! Exiting. \n " , argv [ 0 ] ) ;
2006-06-25 20:48:02 -04:00
/* Note: we don't have to send anything to the old pids because we
* never stopped them . */
exit ( 1 ) ;
}
2010-10-22 10:06:11 -04:00
err = protocol_bind_all ( errmsg , sizeof ( errmsg ) ) ;
if ( ( err & ~ ERR_WARN ) ! = ERR_NONE ) {
if ( ( err & ERR_ALERT ) | | ( err & ERR_WARN ) )
2017-11-24 10:50:31 -05:00
ha_alert ( " [%s.main()] %s. \n " , argv [ 0 ] , errmsg ) ;
2010-10-22 10:06:11 -04:00
2017-11-24 10:50:31 -05:00
ha_alert ( " [%s.main()] Some protocols failed to start their listeners! Exiting. \n " , argv [ 0 ] ) ;
2007-10-16 06:25:14 -04:00
protocol_unbind_all ( ) ; /* cleanup everything we can */
if ( nb_oldpids )
tell_old_pids ( SIGTTIN ) ;
exit ( 1 ) ;
2010-10-22 10:06:11 -04:00
} else if ( err & ERR_WARN ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " [%s.main()] %s. \n " , argv [ 0 ] , errmsg ) ;
2007-10-16 06:25:14 -04:00
}
2017-04-05 16:33:04 -04:00
/* Ok, all listener should now be bound, close any leftover sockets
* the previous process gave us , we don ' t need them anymore
*/
while ( xfer_sock_list ! = NULL ) {
struct xfer_sock_list * tmpxfer = xfer_sock_list - > next ;
close ( xfer_sock_list - > fd ) ;
free ( xfer_sock_list - > iface ) ;
free ( xfer_sock_list - > namespace ) ;
free ( xfer_sock_list ) ;
xfer_sock_list = tmpxfer ;
}
2007-10-16 06:25:14 -04:00
2006-06-25 20:48:02 -04:00
/* prepare pause/play signals */
2010-08-27 11:56:48 -04:00
signal_register_fct ( SIGTTOU , sig_pause , SIGTTOU ) ;
signal_register_fct ( SIGTTIN , sig_listen , SIGTTIN ) ;
2006-06-25 20:48:02 -04:00
/* MODE_QUIET can inhibit alerts and warnings below this line */
2017-12-25 15:03:31 -05:00
if ( getenv ( " HAPROXY_MWORKER_REEXEC " ) ! = NULL ) {
/* either stdin/out/err are already closed or should stay as they are. */
if ( ( global . mode & MODE_DAEMON ) ) {
/* daemon mode re-executing, stdin/stdout/stderr are already closed so keep quiet */
global . mode & = ~ MODE_VERBOSE ;
global . mode | = MODE_QUIET ; /* ensure that we won't say anything from now */
}
} else {
if ( ( global . mode & MODE_QUIET ) & & ! ( global . mode & MODE_VERBOSE ) ) {
/* detach from the tty */
2017-12-28 10:09:36 -05:00
stdio_quiet ( - 1 ) ;
2017-12-25 15:03:31 -05:00
}
2006-06-25 20:48:02 -04:00
}
/* open log & pid files before the chroot */
2017-11-06 05:00:03 -05:00
if ( ( global . mode & MODE_DAEMON | | global . mode & MODE_MWORKER ) & & global . pidfile ! = NULL ) {
2006-06-25 20:48:02 -04:00
unlink ( global . pidfile ) ;
pidfd = open ( global . pidfile , O_CREAT | O_WRONLY | O_TRUNC , 0644 ) ;
if ( pidfd < 0 ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " [%s.main()] Cannot create pidfile %s \n " , argv [ 0 ] , global . pidfile ) ;
2006-06-25 20:48:02 -04:00
if ( nb_oldpids )
tell_old_pids ( SIGTTIN ) ;
2007-10-16 06:25:14 -04:00
protocol_unbind_all ( ) ;
2006-06-25 20:48:02 -04:00
exit ( 1 ) ;
}
}
2007-03-24 12:24:39 -04:00
if ( ( global . last_checks & LSTCHK_NETADM ) & & global . uid ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " [%s.main()] Some configuration options require full privileges, so global.uid cannot be changed. \n "
" " , argv [ 0 ] ) ;
2007-10-16 06:25:14 -04:00
protocol_unbind_all ( ) ;
2007-03-24 12:24:39 -04:00
exit ( 1 ) ;
}
2009-02-04 12:02:48 -05:00
/* If the user is not root, we'll still let him try the configuration
* but we inform him that unexpected behaviour may occur .
*/
if ( ( global . last_checks & LSTCHK_NETADM ) & & getuid ( ) )
2017-11-24 10:50:31 -05:00
ha_warning ( " [%s.main()] Some options which require full privileges "
" might not work well. \n "
" " , argv [ 0 ] ) ;
2009-02-04 12:02:48 -05:00
2017-06-01 11:38:50 -04:00
if ( ( global . mode & ( MODE_MWORKER | MODE_DAEMON ) ) = = 0 ) {
/* chroot if needed */
if ( global . chroot ! = NULL ) {
if ( chroot ( global . chroot ) = = - 1 | | chdir ( " / " ) = = - 1 ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " [%s.main()] Cannot chroot(%s). \n " , argv [ 0 ] , global . chroot ) ;
2017-06-01 11:38:50 -04:00
if ( nb_oldpids )
tell_old_pids ( SIGTTIN ) ;
protocol_unbind_all ( ) ;
exit ( 1 ) ;
}
2007-10-15 12:57:08 -04:00
}
}
2018-11-21 09:48:31 -05:00
if ( nb_oldpids & & ! ( global . mode & MODE_MWORKER_WAIT ) )
2010-08-25 06:58:59 -04:00
nb_oldpids = tell_old_pids ( oldpids_sig ) ;
2006-06-25 20:48:02 -04:00
2017-06-20 05:20:33 -04:00
if ( ( getenv ( " HAPROXY_MWORKER_REEXEC " ) = = NULL ) ) {
nb_oldpids = 0 ;
free ( oldpids ) ;
oldpids = NULL ;
}
2006-06-25 20:48:02 -04:00
/* Note that any error at this stage will be fatal because we will not
* be able to restart the old pids .
*/
2017-06-01 11:38:50 -04:00
if ( ( global . mode & ( MODE_MWORKER | MODE_DAEMON ) ) = = 0 ) {
/* setgid / setuid */
if ( global . gid ) {
if ( getgroups ( 0 , NULL ) > 0 & & setgroups ( 0 , NULL ) = = - 1 )
2017-11-24 10:50:31 -05:00
ha_warning ( " [%s.main()] Failed to drop supplementary groups. Using 'gid'/'group' "
" without 'uid'/'user' is generally useless. \n " , argv [ 0 ] ) ;
2017-06-01 11:38:50 -04:00
if ( setgid ( global . gid ) = = - 1 ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " [%s.main()] Cannot set gid %d. \n " , argv [ 0 ] , global . gid ) ;
2017-06-01 11:38:50 -04:00
protocol_unbind_all ( ) ;
exit ( 1 ) ;
}
}
BUG/MEDIUM: remove supplementary groups when changing gid
Without it, haproxy will retain the group membership of root, which may
give more access than intended to the process. For example, haproxy would
still be in the wheel group on Fedora 18, as seen with :
# haproxy -f /etc/haproxy/haproxy.cfg
# ps a -o pid,user,group,command | grep hapr
3545 haproxy haproxy haproxy -f /etc/haproxy/haproxy.cfg
4356 root root grep --color=auto hapr
# grep Group /proc/3545/status
Groups: 0 1 2 3 4 6 10
# getent group wheel
wheel:x:10:root,misc
[WT: The issue has been investigated by independent security research team
and realized by itself not being able to allow security exploitation.
Additionally, dropping groups is not allowed to unprivileged users,
though this mode of deployment is quite common. Thus a warning is
emitted in this case to inform the user. The fix could be backported
into all supported versions as the issue has always been there. ]
2013-01-12 12:35:19 -05:00
2017-06-01 11:38:50 -04:00
if ( global . uid & & setuid ( global . uid ) = = - 1 ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " [%s.main()] Cannot set uid %d. \n " , argv [ 0 ] , global . uid ) ;
BUG/MEDIUM: remove supplementary groups when changing gid
Without it, haproxy will retain the group membership of root, which may
give more access than intended to the process. For example, haproxy would
still be in the wheel group on Fedora 18, as seen with :
# haproxy -f /etc/haproxy/haproxy.cfg
# ps a -o pid,user,group,command | grep hapr
3545 haproxy haproxy haproxy -f /etc/haproxy/haproxy.cfg
4356 root root grep --color=auto hapr
# grep Group /proc/3545/status
Groups: 0 1 2 3 4 6 10
# getent group wheel
wheel:x:10:root,misc
[WT: The issue has been investigated by independent security research team
and realized by itself not being able to allow security exploitation.
Additionally, dropping groups is not allowed to unprivileged users,
though this mode of deployment is quite common. Thus a warning is
emitted in this case to inform the user. The fix could be backported
into all supported versions as the issue has always been there. ]
2013-01-12 12:35:19 -05:00
protocol_unbind_all ( ) ;
exit ( 1 ) ;
}
2006-06-25 20:48:02 -04:00
}
/* check ulimits */
limit . rlim_cur = limit . rlim_max = 0 ;
getrlimit ( RLIMIT_NOFILE , & limit ) ;
if ( limit . rlim_cur < global . maxsock ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " [%s.main()] FD limit (%d) too low for maxconn=%d/maxsock=%d. Please raise 'ulimit-n' to %d or more to avoid any trouble. \n " ,
argv [ 0 ] , ( int ) limit . rlim_cur , global . maxconn , global . maxsock , global . maxsock ) ;
2006-06-25 20:48:02 -04:00
}
2018-11-21 09:48:31 -05:00
if ( global . mode & ( MODE_DAEMON | MODE_MWORKER | MODE_MWORKER_WAIT ) ) {
2009-02-04 16:05:05 -05:00
struct proxy * px ;
2015-05-01 13:13:41 -04:00
struct peers * curpeers ;
2006-06-25 20:48:02 -04:00
int ret = 0 ;
int proc ;
2017-12-28 10:09:36 -05:00
int devnullfd = - 1 ;
2006-06-25 20:48:02 -04:00
2017-06-01 11:38:50 -04:00
/*
* if daemon + mworker : must fork here to let a master
* process live in background before forking children
*/
2017-06-01 11:38:51 -04:00
if ( ( getenv ( " HAPROXY_MWORKER_REEXEC " ) = = NULL )
& & ( global . mode & MODE_MWORKER )
& & ( global . mode & MODE_DAEMON ) ) {
2017-06-01 11:38:50 -04:00
ret = fork ( ) ;
if ( ret < 0 ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " [%s.main()] Cannot fork. \n " , argv [ 0 ] ) ;
2017-06-01 11:38:50 -04:00
protocol_unbind_all ( ) ;
exit ( 1 ) ; /* there has been an error */
2018-07-04 09:31:23 -04:00
} else if ( ret > 0 ) { /* parent leave to daemonize */
2017-06-01 11:38:50 -04:00
exit ( 0 ) ;
2018-07-04 09:31:23 -04:00
} else /* change the process group ID in the child (master process) */
setsid ( ) ;
2017-06-01 11:38:50 -04:00
}
2017-06-01 11:38:55 -04:00
2017-11-06 05:00:04 -05:00
/* if in master-worker mode, write the PID of the father */
if ( global . mode & MODE_MWORKER ) {
char pidstr [ 100 ] ;
snprintf ( pidstr , sizeof ( pidstr ) , " %d \n " , getpid ( ) ) ;
2018-01-23 13:20:19 -05:00
if ( pidfd > = 0 )
shut_your_big_mouth_gcc ( write ( pidfd , pidstr , strlen ( pidstr ) ) ) ;
2017-11-06 05:00:04 -05:00
}
2006-06-25 20:48:02 -04:00
/* the father launches the required number of processes */
2018-11-21 09:48:31 -05:00
if ( ! ( global . mode & MODE_MWORKER_WAIT ) ) {
children = calloc ( global . nbproc , sizeof ( int ) ) ;
for ( proc = 0 ; proc < global . nbproc ; proc + + ) {
ret = fork ( ) ;
if ( ret < 0 ) {
ha_alert ( " [%s.main()] Cannot fork. \n " , argv [ 0 ] ) ;
protocol_unbind_all ( ) ;
exit ( 1 ) ; /* there has been an error */
}
else if ( ret = = 0 ) /* child breaks here */
break ;
children [ proc ] = ret ;
if ( pidfd > = 0 & & ! ( global . mode & MODE_MWORKER ) ) {
char pidstr [ 100 ] ;
snprintf ( pidstr , sizeof ( pidstr ) , " %d \n " , ret ) ;
shut_your_big_mouth_gcc ( write ( pidfd , pidstr , strlen ( pidstr ) ) ) ;
}
if ( global . mode & MODE_MWORKER ) {
struct mworker_proc * child ;
2018-11-21 12:04:53 -05:00
ha_notice ( " New worker #%d (%d) forked \n " , relative_pid , ret ) ;
2018-11-21 09:48:31 -05:00
/* find the right mworker_proc */
list_for_each_entry ( child , & proc_list , list ) {
if ( child - > relative_pid = = relative_pid & &
child - > reloads = = 0 ) {
child - > timestamp = now . tv_sec ;
child - > pid = ret ;
break ;
}
2018-10-26 08:47:30 -04:00
}
}
2018-09-11 04:06:26 -04:00
2018-11-21 09:48:31 -05:00
relative_pid + + ; /* each child will get a different one */
pid_bit < < = 1 ;
}
} else {
/* wait mode */
global . nbproc = 1 ;
proc = 1 ;
2006-06-25 20:48:02 -04:00
}
2012-11-16 10:12:27 -05:00
# ifdef USE_CPU_AFFINITY
if ( proc < global . nbproc & & /* child */
2015-04-20 05:36:57 -04:00
proc < LONGBITS & & /* only the first 32/64 processes may be pinned */
2017-11-22 10:50:41 -05:00
global . cpu_map . proc [ proc ] ) /* only do this if the process has a CPU map */
2015-09-17 15:26:40 -04:00
# ifdef __FreeBSD__
2017-08-16 11:29:11 -04:00
{
cpuset_t cpuset ;
int i ;
2017-11-22 10:50:41 -05:00
unsigned long cpu_map = global . cpu_map . proc [ proc ] ;
2017-08-16 11:29:11 -04:00
CPU_ZERO ( & cpuset ) ;
while ( ( i = ffsl ( cpu_map ) ) > 0 ) {
CPU_SET ( i - 1 , & cpuset ) ;
2018-03-12 16:47:39 -04:00
cpu_map & = ~ ( 1UL < < ( i - 1 ) ) ;
2017-08-16 11:29:11 -04:00
}
ret = cpuset_setaffinity ( CPU_LEVEL_WHICH , CPU_WHICH_PID , - 1 , sizeof ( cpuset ) , & cpuset ) ;
}
2015-09-17 15:26:40 -04:00
# else
2017-11-22 10:50:41 -05:00
sched_setaffinity ( 0 , sizeof ( unsigned long ) , ( void * ) & global . cpu_map . proc [ proc ] ) ;
2015-09-17 15:26:40 -04:00
# endif
2012-11-16 10:12:27 -05:00
# endif
2006-06-25 20:48:02 -04:00
/* close the pidfile both in children and father */
2012-09-05 02:02:48 -04:00
if ( pidfd > = 0 ) {
//lseek(pidfd, 0, SEEK_SET); /* debug: emulate eglibc bug */
close ( pidfd ) ;
}
2010-08-25 06:49:05 -04:00
/* We won't ever use this anymore */
free ( global . pidfile ) ; global . pidfile = NULL ;
2006-06-25 20:48:02 -04:00
2015-05-01 11:01:08 -04:00
if ( proc = = global . nbproc ) {
2018-11-21 09:48:31 -05:00
if ( global . mode & ( MODE_MWORKER | MODE_MWORKER_WAIT ) ) {
2017-11-28 17:26:08 -05:00
if ( ( ! ( global . mode & MODE_QUIET ) | | ( global . mode & MODE_VERBOSE ) ) & &
( global . mode & MODE_DAEMON ) ) {
/* detach from the tty, this is required to properly daemonize. */
2017-12-28 10:09:36 -05:00
if ( ( getenv ( " HAPROXY_MWORKER_REEXEC " ) = = NULL ) )
stdio_quiet ( - 1 ) ;
2017-11-28 17:26:08 -05:00
global . mode & = ~ MODE_VERBOSE ;
global . mode | = MODE_QUIET ; /* ensure that we won't say anything from now */
}
2018-09-11 04:06:18 -04:00
mworker_loop ( ) ;
2017-06-07 09:04:47 -04:00
/* should never get there */
exit ( EXIT_FAILURE ) ;
2015-05-01 11:01:08 -04:00
}
2017-06-08 13:05:48 -04:00
# if defined(USE_OPENSSL) && !defined(OPENSSL_NO_DH)
2017-01-20 20:10:18 -05:00
ssl_free_dh ( ) ;
# endif
2017-06-07 09:04:47 -04:00
exit ( 0 ) ; /* parent must leave */
2015-05-01 11:01:08 -04:00
}
2017-06-01 11:38:52 -04:00
/* child must never use the atexit function */
atexit_flag = 0 ;
2018-09-11 04:06:26 -04:00
/* close useless master sockets */
if ( global . mode & MODE_MWORKER ) {
struct mworker_proc * child , * it ;
master = 0 ;
2018-10-26 08:47:45 -04:00
mworker_cli_proxy_stop ( ) ;
2018-09-11 04:06:26 -04:00
/* free proc struct of other processes */
list_for_each_entry_safe ( child , it , & proc_list , list ) {
2018-10-26 08:47:30 -04:00
/* close the FD of the master side for all
* workers , we don ' t need to close the worker
* side of other workers since it ' s done with
* the bind_proc */
2018-11-25 14:03:39 -05:00
if ( child - > ipc_fd [ 0 ] > = 0 )
close ( child - > ipc_fd [ 0 ] ) ;
2018-10-26 08:47:30 -04:00
if ( child - > relative_pid = = relative_pid & &
child - > reloads = = 0 ) {
/* keep this struct if this is our pid */
proc_self = child ;
2018-09-11 04:06:26 -04:00
continue ;
2018-10-26 08:47:30 -04:00
}
2018-09-11 04:06:26 -04:00
LIST_DEL ( & child - > list ) ;
free ( child ) ;
}
}
BUG/MEDIUM: threads/mworker: fix a race on startup
Marc Fournier reported an interesting case when using threads with the
master-worker mode : sometimes, a listener would have its FD closed
during startup. Sometimes it could even be health checks seeing this.
What happens is that after the threads are created, and the pollers
enabled on each threads, the master-worker pipe is registered, and at
the same time a close() is performed on the write side of this pipe
since the children must not use it.
But since this is replicated in every thread, what happens is that the
first thread closes the pipe, thus releases the FD, and the next thread
starting a listener in parallel gets this FD reassigned. Then another
thread closes the FD again, which this time corresponds to the listener.
It can also happen with the health check sockets if they're started
early enough.
This patch splits the mworker_pipe_register() function in two, so that
the close() of the write side of the FD is performed very early after the
fork() and long before threads are created (we don't need to delay it
anyway). Only the pipe registration is done in the threaded code since
it is important that the pollers are properly allocated for this.
The mworker_pipe_register() function now takes care of registering the
pipe only once, and this is guaranteed by a new surrounding lock.
The call to protocol_enable_all() looks fragile in theory since it
scans the list of proxies and their listeners, though in practice
all threads scan the same list and take the same locks for each
listener so it's not possible that any of them escapes the process
and finishes before all listeners are started. And the operation is
idempotent.
This fix must be backported to 1.8. Thanks to Marc for providing very
detailed traces clearly showing the problem.
2018-01-23 13:01:49 -05:00
2017-12-28 10:09:36 -05:00
if ( ! ( global . mode & MODE_QUIET ) | | ( global . mode & MODE_VERBOSE ) ) {
devnullfd = open ( " /dev/null " , O_RDWR , 0 ) ;
if ( devnullfd < 0 ) {
ha_alert ( " Cannot open /dev/null \n " ) ;
exit ( EXIT_FAILURE ) ;
}
}
2017-06-01 11:38:50 -04:00
/* Must chroot and setgid/setuid in the children */
/* chroot if needed */
if ( global . chroot ! = NULL ) {
if ( chroot ( global . chroot ) = = - 1 | | chdir ( " / " ) = = - 1 ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " [%s.main()] Cannot chroot1(%s). \n " , argv [ 0 ] , global . chroot ) ;
2017-06-01 11:38:50 -04:00
if ( nb_oldpids )
tell_old_pids ( SIGTTIN ) ;
protocol_unbind_all ( ) ;
exit ( 1 ) ;
}
}
free ( global . chroot ) ;
global . chroot = NULL ;
/* setgid / setuid */
if ( global . gid ) {
if ( getgroups ( 0 , NULL ) > 0 & & setgroups ( 0 , NULL ) = = - 1 )
2017-11-24 10:50:31 -05:00
ha_warning ( " [%s.main()] Failed to drop supplementary groups. Using 'gid'/'group' "
" without 'uid'/'user' is generally useless. \n " , argv [ 0 ] ) ;
2017-06-01 11:38:50 -04:00
if ( setgid ( global . gid ) = = - 1 ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " [%s.main()] Cannot set gid %d. \n " , argv [ 0 ] , global . gid ) ;
2017-06-01 11:38:50 -04:00
protocol_unbind_all ( ) ;
exit ( 1 ) ;
}
}
if ( global . uid & & setuid ( global . uid ) = = - 1 ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " [%s.main()] Cannot set uid %d. \n " , argv [ 0 ] , global . uid ) ;
2017-06-01 11:38:50 -04:00
protocol_unbind_all ( ) ;
exit ( 1 ) ;
}
2017-05-26 12:19:55 -04:00
/* pass through every cli socket, and check if it's bound to
* the current process and if it exposes listeners sockets .
* Caution : the GTUNE_SOCKET_TRANSFER is now set after the fork .
* */
if ( global . stats_fe ) {
struct bind_conf * bind_conf ;
list_for_each_entry ( bind_conf , & global . stats_fe - > conf . bind , by_fe ) {
if ( bind_conf - > level & ACCESS_FD_LISTENERS ) {
if ( ! bind_conf - > bind_proc | | bind_conf - > bind_proc & ( 1UL < < proc ) ) {
global . tune . options | = GTUNE_SOCKET_TRANSFER ;
break ;
}
}
}
}
2009-02-04 16:05:05 -05:00
/* we might have to unbind some proxies from some processes */
2017-11-24 10:54:05 -05:00
px = proxies_list ;
2009-02-04 16:05:05 -05:00
while ( px ! = NULL ) {
if ( px - > bind_proc & & px - > state ! = PR_STSTOPPED ) {
2017-04-05 19:05:05 -04:00
if ( ! ( px - > bind_proc & ( 1UL < < proc ) ) ) {
if ( global . tune . options & GTUNE_SOCKET_TRANSFER )
zombify_proxy ( px ) ;
else
stop_proxy ( px ) ;
}
2009-02-04 16:05:05 -05:00
}
px = px - > next ;
}
2015-05-01 13:13:41 -04:00
/* we might have to unbind some peers sections from some processes */
2017-07-13 03:07:09 -04:00
for ( curpeers = cfg_peers ; curpeers ; curpeers = curpeers - > next ) {
2015-05-01 13:13:41 -04:00
if ( ! curpeers - > peers_fe )
continue ;
if ( curpeers - > peers_fe - > bind_proc & ( 1UL < < proc ) )
continue ;
stop_proxy ( curpeers - > peers_fe ) ;
/* disable this peer section so that it kills itself */
2015-09-28 10:39:25 -04:00
signal_unregister_handler ( curpeers - > sighandler ) ;
task_delete ( curpeers - > sync_task ) ;
task_free ( curpeers - > sync_task ) ;
curpeers - > sync_task = NULL ;
task_free ( curpeers - > peers_fe - > task ) ;
curpeers - > peers_fe - > task = NULL ;
2015-05-01 13:13:41 -04:00
curpeers - > peers_fe = NULL ;
}
2018-11-13 10:18:23 -05:00
/*
* This is only done in daemon mode because we might want the
* logs on stdout in mworker mode . If we ' re NOT in QUIET mode ,
* we should now close the 3 first FDs to ensure that we can
* detach from the TTY . We MUST NOT do it in other cases since
* it would have already be done , and 0 - 2 would have been
* affected to listening sockets
2006-06-25 20:48:02 -04:00
*/
2018-11-13 10:18:23 -05:00
if ( ( global . mode & MODE_DAEMON ) & &
( ! ( global . mode & MODE_QUIET ) | | ( global . mode & MODE_VERBOSE ) ) ) {
2006-06-25 20:48:02 -04:00
/* detach from the tty */
2017-12-28 10:09:36 -05:00
stdio_quiet ( devnullfd ) ;
2008-11-16 01:40:34 -05:00
global . mode & = ~ MODE_VERBOSE ;
2006-06-25 20:48:02 -04:00
global . mode | = MODE_QUIET ; /* ensure that we won't say anything from now */
}
pid = getpid ( ) ; /* update child's pid */
2018-07-04 09:31:23 -04:00
if ( ! ( global . mode & MODE_MWORKER ) ) /* in mworker mode we don't want a new pgid for the children */
setsid ( ) ;
2007-04-09 13:29:56 -04:00
fork_poller ( ) ;
2006-06-25 20:48:02 -04:00
}
2017-10-24 07:53:54 -04:00
global . mode & = ~ MODE_STARTING ;
2007-04-08 10:39:58 -04:00
/*
* That ' s it : the central polling loop . Run until we stop .
*/
MAJOR: threads: Start threads to experiment multithreading
[WARNING] For now, HAProxy is not thread-safe, so from this commit, it will be
broken for a while, when compiled with threads.
When nbthread parameter is greater than 1, HAProxy will create the corresponding
number of threads. If nbthread is set to 1, nothing should be done. So if there
are concurrency issues (and be sure there will be, unfortunatly), an obvious
workaround is to disable the multithreading...
Each created threads will run a polling loop. So, in a certain way, it is pretty
similar to the nbproc mode ("outside" the bugs and the lock
contention). Nevertheless, there are an init and a deinit steps for each thread
to deal with per-thread allocation.
Each thread has a tid (thread-id), numbered from 0 to (nbtread-1). It is used in
many place to do bitwise operations or to improve debugging information.
2017-08-29 09:38:48 -04:00
# ifdef USE_THREAD
2017-10-27 07:53:47 -04:00
{
MAJOR: threads: Start threads to experiment multithreading
[WARNING] For now, HAProxy is not thread-safe, so from this commit, it will be
broken for a while, when compiled with threads.
When nbthread parameter is greater than 1, HAProxy will create the corresponding
number of threads. If nbthread is set to 1, nothing should be done. So if there
are concurrency issues (and be sure there will be, unfortunatly), an obvious
workaround is to disable the multithreading...
Each created threads will run a polling loop. So, in a certain way, it is pretty
similar to the nbproc mode ("outside" the bugs and the lock
contention). Nevertheless, there are an init and a deinit steps for each thread
to deal with per-thread allocation.
Each thread has a tid (thread-id), numbered from 0 to (nbtread-1). It is used in
many place to do bitwise operations or to improve debugging information.
2017-08-29 09:38:48 -04:00
unsigned int * tids = calloc ( global . nbthread , sizeof ( unsigned int ) ) ;
pthread_t * threads = calloc ( global . nbthread , sizeof ( pthread_t ) ) ;
int i ;
2018-06-07 03:46:01 -04:00
sigset_t blocked_sig , old_sig ;
MAJOR: threads: Start threads to experiment multithreading
[WARNING] For now, HAProxy is not thread-safe, so from this commit, it will be
broken for a while, when compiled with threads.
When nbthread parameter is greater than 1, HAProxy will create the corresponding
number of threads. If nbthread is set to 1, nothing should be done. So if there
are concurrency issues (and be sure there will be, unfortunatly), an obvious
workaround is to disable the multithreading...
Each created threads will run a polling loop. So, in a certain way, it is pretty
similar to the nbproc mode ("outside" the bugs and the lock
contention). Nevertheless, there are an init and a deinit steps for each thread
to deal with per-thread allocation.
Each thread has a tid (thread-id), numbered from 0 to (nbtread-1). It is used in
many place to do bitwise operations or to improve debugging information.
2017-08-29 09:38:48 -04:00
2017-10-27 07:53:47 -04:00
/* Init tids array */
for ( i = 0 ; i < global . nbthread ; i + + )
MAJOR: threads: Start threads to experiment multithreading
[WARNING] For now, HAProxy is not thread-safe, so from this commit, it will be
broken for a while, when compiled with threads.
When nbthread parameter is greater than 1, HAProxy will create the corresponding
number of threads. If nbthread is set to 1, nothing should be done. So if there
are concurrency issues (and be sure there will be, unfortunatly), an obvious
workaround is to disable the multithreading...
Each created threads will run a polling loop. So, in a certain way, it is pretty
similar to the nbproc mode ("outside" the bugs and the lock
contention). Nevertheless, there are an init and a deinit steps for each thread
to deal with per-thread allocation.
Each thread has a tid (thread-id), numbered from 0 to (nbtread-1). It is used in
many place to do bitwise operations or to improve debugging information.
2017-08-29 09:38:48 -04:00
tids [ i ] = i ;
2017-10-27 07:53:47 -04:00
2018-06-07 03:46:01 -04:00
/* ensure the signals will be blocked in every thread */
sigfillset ( & blocked_sig ) ;
sigdelset ( & blocked_sig , SIGPROF ) ;
sigdelset ( & blocked_sig , SIGBUS ) ;
sigdelset ( & blocked_sig , SIGFPE ) ;
sigdelset ( & blocked_sig , SIGILL ) ;
sigdelset ( & blocked_sig , SIGSEGV ) ;
pthread_sigmask ( SIG_SETMASK , & blocked_sig , & old_sig ) ;
2017-10-27 07:53:47 -04:00
/* Create nbthread-1 thread. The first thread is the current process */
threads [ 0 ] = pthread_self ( ) ;
for ( i = 1 ; i < global . nbthread ; i + + )
MAJOR: threads: Start threads to experiment multithreading
[WARNING] For now, HAProxy is not thread-safe, so from this commit, it will be
broken for a while, when compiled with threads.
When nbthread parameter is greater than 1, HAProxy will create the corresponding
number of threads. If nbthread is set to 1, nothing should be done. So if there
are concurrency issues (and be sure there will be, unfortunatly), an obvious
workaround is to disable the multithreading...
Each created threads will run a polling loop. So, in a certain way, it is pretty
similar to the nbproc mode ("outside" the bugs and the lock
contention). Nevertheless, there are an init and a deinit steps for each thread
to deal with per-thread allocation.
Each thread has a tid (thread-id), numbered from 0 to (nbtread-1). It is used in
many place to do bitwise operations or to improve debugging information.
2017-08-29 09:38:48 -04:00
pthread_create ( & threads [ i ] , NULL , & run_thread_poll_loop , & tids [ i ] ) ;
2017-10-27 07:53:47 -04:00
2017-10-16 09:49:32 -04:00
# ifdef USE_CPU_AFFINITY
2017-10-27 07:53:47 -04:00
/* Now the CPU affinity for all threads */
for ( i = 0 ; i < global . nbthread ; i + + ) {
2017-11-22 10:50:41 -05:00
if ( global . cpu_map . proc [ relative_pid - 1 ] )
global . cpu_map . thread [ relative_pid - 1 ] [ i ] & = global . cpu_map . proc [ relative_pid - 1 ] ;
2017-10-16 09:49:32 -04:00
2018-01-20 12:19:22 -05:00
if ( i < MAX_THREADS & & /* only the first 32/64 threads may be pinned */
2017-12-01 12:19:43 -05:00
global . cpu_map . thread [ relative_pid - 1 ] [ i ] ) { /* only do this if the thread has a THREAD map */
# if defined(__FreeBSD__) || defined(__NetBSD__)
cpuset_t cpuset ;
# else
cpu_set_t cpuset ;
# endif
int j ;
unsigned long cpu_map = global . cpu_map . thread [ relative_pid - 1 ] [ i ] ;
CPU_ZERO ( & cpuset ) ;
while ( ( j = ffsl ( cpu_map ) ) > 0 ) {
CPU_SET ( j - 1 , & cpuset ) ;
2018-03-12 16:47:39 -04:00
cpu_map & = ~ ( 1UL < < ( j - 1 ) ) ;
2017-12-01 12:19:43 -05:00
}
2017-10-16 09:49:32 -04:00
pthread_setaffinity_np ( threads [ i ] ,
2017-12-01 12:19:43 -05:00
sizeof ( cpuset ) , & cpuset ) ;
}
MAJOR: threads: Start threads to experiment multithreading
[WARNING] For now, HAProxy is not thread-safe, so from this commit, it will be
broken for a while, when compiled with threads.
When nbthread parameter is greater than 1, HAProxy will create the corresponding
number of threads. If nbthread is set to 1, nothing should be done. So if there
are concurrency issues (and be sure there will be, unfortunatly), an obvious
workaround is to disable the multithreading...
Each created threads will run a polling loop. So, in a certain way, it is pretty
similar to the nbproc mode ("outside" the bugs and the lock
contention). Nevertheless, there are an init and a deinit steps for each thread
to deal with per-thread allocation.
Each thread has a tid (thread-id), numbered from 0 to (nbtread-1). It is used in
many place to do bitwise operations or to improve debugging information.
2017-08-29 09:38:48 -04:00
}
2017-10-27 07:53:47 -04:00
# endif /* !USE_CPU_AFFINITY */
2018-06-07 03:46:01 -04:00
/* when multithreading we need to let only the thread 0 handle the signals */
2018-09-11 04:06:23 -04:00
haproxy_unblock_signals ( ) ;
2018-06-07 03:46:01 -04:00
2017-10-27 07:53:47 -04:00
/* Finally, start the poll loop for the first thread */
run_thread_poll_loop ( & tids [ 0 ] ) ;
/* Wait the end of other threads */
for ( i = 1 ; i < global . nbthread ; i + + )
MAJOR: threads: Start threads to experiment multithreading
[WARNING] For now, HAProxy is not thread-safe, so from this commit, it will be
broken for a while, when compiled with threads.
When nbthread parameter is greater than 1, HAProxy will create the corresponding
number of threads. If nbthread is set to 1, nothing should be done. So if there
are concurrency issues (and be sure there will be, unfortunatly), an obvious
workaround is to disable the multithreading...
Each created threads will run a polling loop. So, in a certain way, it is pretty
similar to the nbproc mode ("outside" the bugs and the lock
contention). Nevertheless, there are an init and a deinit steps for each thread
to deal with per-thread allocation.
Each thread has a tid (thread-id), numbered from 0 to (nbtread-1). It is used in
many place to do bitwise operations or to improve debugging information.
2017-08-29 09:38:48 -04:00
pthread_join ( threads [ i ] , NULL ) ;
free ( tids ) ;
free ( threads ) ;
2006-06-25 20:48:02 -04:00
2017-05-30 09:34:30 -04:00
# if defined(DEBUG_THREAD) || defined(DEBUG_FULL)
show_lock_stats ( ) ;
# endif
MAJOR: threads: Start threads to experiment multithreading
[WARNING] For now, HAProxy is not thread-safe, so from this commit, it will be
broken for a while, when compiled with threads.
When nbthread parameter is greater than 1, HAProxy will create the corresponding
number of threads. If nbthread is set to 1, nothing should be done. So if there
are concurrency issues (and be sure there will be, unfortunatly), an obvious
workaround is to disable the multithreading...
Each created threads will run a polling loop. So, in a certain way, it is pretty
similar to the nbproc mode ("outside" the bugs and the lock
contention). Nevertheless, there are an init and a deinit steps for each thread
to deal with per-thread allocation.
Each thread has a tid (thread-id), numbered from 0 to (nbtread-1). It is used in
many place to do bitwise operations or to improve debugging information.
2017-08-29 09:38:48 -04:00
}
2017-10-27 07:53:47 -04:00
# else /* ! USE_THREAD */
2018-09-11 04:06:23 -04:00
haproxy_unblock_signals ( ) ;
2017-10-27 07:53:47 -04:00
run_thread_poll_loop ( ( int [ ] ) { 0 } ) ;
2017-10-16 09:49:32 -04:00
# endif
MAJOR: threads: Start threads to experiment multithreading
[WARNING] For now, HAProxy is not thread-safe, so from this commit, it will be
broken for a while, when compiled with threads.
When nbthread parameter is greater than 1, HAProxy will create the corresponding
number of threads. If nbthread is set to 1, nothing should be done. So if there
are concurrency issues (and be sure there will be, unfortunatly), an obvious
workaround is to disable the multithreading...
Each created threads will run a polling loop. So, in a certain way, it is pretty
similar to the nbproc mode ("outside" the bugs and the lock
contention). Nevertheless, there are an init and a deinit steps for each thread
to deal with per-thread allocation.
Each thread has a tid (thread-id), numbered from 0 to (nbtread-1). It is used in
many place to do bitwise operations or to improve debugging information.
2017-08-29 09:38:48 -04:00
/* Do some cleanup */
2006-06-25 20:48:02 -04:00
deinit ( ) ;
MAJOR: threads: Start threads to experiment multithreading
[WARNING] For now, HAProxy is not thread-safe, so from this commit, it will be
broken for a while, when compiled with threads.
When nbthread parameter is greater than 1, HAProxy will create the corresponding
number of threads. If nbthread is set to 1, nothing should be done. So if there
are concurrency issues (and be sure there will be, unfortunatly), an obvious
workaround is to disable the multithreading...
Each created threads will run a polling loop. So, in a certain way, it is pretty
similar to the nbproc mode ("outside" the bugs and the lock
contention). Nevertheless, there are an init and a deinit steps for each thread
to deal with per-thread allocation.
Each thread has a tid (thread-id), numbered from 0 to (nbtread-1). It is used in
many place to do bitwise operations or to improve debugging information.
2017-08-29 09:38:48 -04:00
2006-06-25 20:48:02 -04:00
exit ( 0 ) ;
}
/*
* Local variables :
* c - indent - level : 8
* c - basic - offset : 8
* End :
*/