From: Daan De Meyer Date: Fri, 17 Apr 2026 14:06:23 +0000 (+0200) Subject: shared/curl-util: load libcurl via dlopen X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c99e93ce0a195eafe988b1769729c526ca68352b;p=thirdparty%2Fsystemd.git shared/curl-util: load libcurl via dlopen Convert curl-util to the dlopen pattern used by other optional shared libraries in libshared (libarchive, pcre2, idn, ...). Declare the curl API entry points with DLSYM_PROTOTYPE, resolve them in a dlopen_curl() helper, and call the sym_* wrappers from callers. curl_glue_new() now loads the library on first use, so consumers going through CurlGlue pick this up automatically; journal-upload and report-upload call dlopen_curl() directly since they use curl without the glue layer. With this in place curl-util can live in libshared itself, linked only against libcurl's headers (via libcurl_cflags). The libcurlutil_static convenience library and the libcurl link dependency on systemd-imdsd, systemd-pull, systemd-journal-upload and systemd-report go away. Also move the easy_setopt() helper macro next to the DLSYM declarations so all consumers use a single sym-prefixed definition, and add a dlopen_curl() check to test-dlopen-so. --- diff --git a/meson.build b/meson.build index e0e7102a39f..08753903799 100644 --- a/meson.build +++ b/meson.build @@ -1296,6 +1296,7 @@ endforeach libcurl = dependency('libcurl', version : '>= 7.32.0', required : get_option('libcurl')) +libcurl_cflags = libcurl.partial_dependency(includes: true, compile_args: true) conf.set10('HAVE_LIBCURL', libcurl.found()) conf.set10('CURL_NO_OLDIES', conf.get('BUILD_MODE_DEVELOPER') == 1) diff --git a/src/imds/imdsd.c b/src/imds/imdsd.c index 5ed7d1df338..211565880c2 100644 --- a/src/imds/imdsd.c +++ b/src/imds/imdsd.c @@ -257,9 +257,9 @@ static void context_reset_for_refresh(Context *c) { c->curl_data = NULL; } - curl_slist_free_all(c->request_header_token); + sym_curl_slist_free_all(c->request_header_token); c->request_header_token = NULL; - curl_slist_free_all(c->request_header_data); + sym_curl_slist_free_all(c->request_header_data); c->request_header_data = NULL; c->cache_fd = safe_close(c->cache_fd); @@ -723,9 +723,9 @@ static int context_acquire_http_status(Context *c, CURL *curl, long *ret_status) */ long status; - CURLcode code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); + CURLcode code = sym_curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); if (code != CURLE_OK) - return context_log_errno(c, LOG_ERR, SYNTHETIC_ERRNO(EIO), "Failed to retrieve response code: %s", curl_easy_strerror(code)); + return context_log_errno(c, LOG_ERR, SYNTHETIC_ERRNO(EIO), "Failed to retrieve response code: %s", sym_curl_easy_strerror(code)); context_log(c, LOG_DEBUG, "Got HTTP error code %li.", status); @@ -906,7 +906,7 @@ static void curl_glue_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { /* Called whenever libcurl did its thing and reports a download being complete or having failed */ Context *c = NULL; - if (curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char**) &c) != CURLE_OK) + if (sym_curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char**) &c) != CURLE_OK) return; switch (result) { @@ -927,7 +927,7 @@ static void curl_glue_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { case CURLE_GOT_NOTHING: case CURLE_SEND_ERROR: case CURLE_RECV_ERROR: - context_log(c, LOG_INFO, "Connection error from curl: %s", curl_easy_strerror(result)); + context_log(c, LOG_INFO, "Connection error from curl: %s", sym_curl_easy_strerror(result)); /* Automatically retry on some transient errors from curl itself */ r = context_schedule_retry(c); @@ -939,7 +939,7 @@ static void curl_glue_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { default: return context_fail_full( c, - context_log_errno(c, LOG_ERR, SYNTHETIC_ERRNO(EHOSTDOWN), "Transfer failed: %s", curl_easy_strerror(result)), + context_log_errno(c, LOG_ERR, SYNTHETIC_ERRNO(EHOSTDOWN), "Transfer failed: %s", sym_curl_easy_strerror(result)), "io.systemd.InstanceMetadata.CommunicationFailure"); } @@ -1122,25 +1122,25 @@ static int context_acquire_data(Context *c) { return context_log_errno(c, LOG_ERR, r, "Failed to create curl header: %m"); if (c->request_header_data) - if (curl_easy_setopt(c->curl_data, CURLOPT_HTTPHEADER, c->request_header_data) != CURLE_OK) + if (sym_curl_easy_setopt(c->curl_data, CURLOPT_HTTPHEADER, c->request_header_data) != CURLE_OK) return context_log_errno(c, LOG_ERR, SYNTHETIC_ERRNO(EIO), "Failed to set HTTP request header."); - if (curl_easy_setopt(c->curl_data, CURLOPT_WRITEFUNCTION, data_write_callback) != CURLE_OK) + if (sym_curl_easy_setopt(c->curl_data, CURLOPT_WRITEFUNCTION, data_write_callback) != CURLE_OK) return context_log_errno(c, LOG_ERR, SYNTHETIC_ERRNO(EIO), "Failed to set CURL write function."); - if (curl_easy_setopt(c->curl_data, CURLOPT_WRITEDATA, c) != CURLE_OK) + if (sym_curl_easy_setopt(c->curl_data, CURLOPT_WRITEDATA, c) != CURLE_OK) return context_log_errno(c, LOG_ERR, SYNTHETIC_ERRNO(EIO), "Failed to set CURL write function userdata."); - if (curl_easy_setopt(c->curl_data, CURLOPT_SOCKOPTFUNCTION, setsockopt_callback) != CURLE_OK) + if (sym_curl_easy_setopt(c->curl_data, CURLOPT_SOCKOPTFUNCTION, setsockopt_callback) != CURLE_OK) return context_log_errno(c, LOG_ERR, SYNTHETIC_ERRNO(EIO), "Failed to set CURL setsockopt function."); - if (curl_easy_setopt(c->curl_data, CURLOPT_SOCKOPTDATA, c) != CURLE_OK) + if (sym_curl_easy_setopt(c->curl_data, CURLOPT_SOCKOPTDATA, c) != CURLE_OK) return context_log_errno(c, LOG_ERR, SYNTHETIC_ERRNO(EIO), "Failed to set CURL setsockopt function userdata."); - if (curl_easy_setopt(c->curl_data, CURLOPT_LOCALPORT, 1L) != CURLE_OK) + if (sym_curl_easy_setopt(c->curl_data, CURLOPT_LOCALPORT, 1L) != CURLE_OK) return context_log_errno(c, LOG_ERR, SYNTHETIC_ERRNO(EIO), "Failed to set CURL setsockopt local port"); - if (curl_easy_setopt(c->curl_data, CURLOPT_LOCALPORTRANGE, 1023L) != CURLE_OK) + if (sym_curl_easy_setopt(c->curl_data, CURLOPT_LOCALPORTRANGE, 1023L) != CURLE_OK) return context_log_errno(c, LOG_ERR, SYNTHETIC_ERRNO(EIO), "Failed to set CURL setsockopt local port range"); r = curl_glue_add(c->glue, c->curl_data); @@ -1216,22 +1216,22 @@ static int context_acquire_token(Context *c) { return context_log_oom(c); } - if (curl_easy_setopt(c->curl_token, CURLOPT_HTTPHEADER, c->request_header_token) != CURLE_OK) + if (sym_curl_easy_setopt(c->curl_token, CURLOPT_HTTPHEADER, c->request_header_token) != CURLE_OK) return context_log_errno(c, LOG_ERR, SYNTHETIC_ERRNO(EIO), "Failed to set HTTP request header."); - if (curl_easy_setopt(c->curl_token, CURLOPT_CUSTOMREQUEST, "PUT") != CURLE_OK) + if (sym_curl_easy_setopt(c->curl_token, CURLOPT_CUSTOMREQUEST, "PUT") != CURLE_OK) return context_log_errno(c, LOG_ERR, SYNTHETIC_ERRNO(EIO), "Failed to set HTTP request method."); - if (curl_easy_setopt(c->curl_token, CURLOPT_WRITEFUNCTION, token_write_callback) != CURLE_OK) + if (sym_curl_easy_setopt(c->curl_token, CURLOPT_WRITEFUNCTION, token_write_callback) != CURLE_OK) return context_log_errno(c, LOG_ERR, SYNTHETIC_ERRNO(EIO), "Failed to set CURL write function."); - if (curl_easy_setopt(c->curl_token, CURLOPT_WRITEDATA, c) != CURLE_OK) + if (sym_curl_easy_setopt(c->curl_token, CURLOPT_WRITEDATA, c) != CURLE_OK) return context_log_errno(c, LOG_ERR, SYNTHETIC_ERRNO(EIO), "Failed to set CURL write function userdata."); - if (curl_easy_setopt(c->curl_token, CURLOPT_SOCKOPTFUNCTION, setsockopt_callback) != CURLE_OK) + if (sym_curl_easy_setopt(c->curl_token, CURLOPT_SOCKOPTFUNCTION, setsockopt_callback) != CURLE_OK) return context_log_errno(c, LOG_ERR, SYNTHETIC_ERRNO(EIO), "Failed to set CURL setsockopt function."); - if (curl_easy_setopt(c->curl_token, CURLOPT_SOCKOPTDATA, c) != CURLE_OK) + if (sym_curl_easy_setopt(c->curl_token, CURLOPT_SOCKOPTDATA, c) != CURLE_OK) return context_log_errno(c, LOG_ERR, SYNTHETIC_ERRNO(EIO), "Failed to set CURL setsockopt function userdata."); r = curl_glue_add(c->glue, c->curl_token); @@ -3070,6 +3070,10 @@ static int run(int argc, char* argv[]) { if (r <= 0) return r; + r = dlopen_curl(); + if (r < 0) + return r; + r = environment_server_info(); if (r < 0) return r; diff --git a/src/imds/meson.build b/src/imds/meson.build index 29fa878eaba..9295c8cf620 100644 --- a/src/imds/meson.build +++ b/src/imds/meson.build @@ -8,27 +8,19 @@ executables += [ libexec_template + { 'name' : 'systemd-imdsd', 'public' : true, - 'sources' : files( - 'imdsd.c', - 'imds-util.c' - ), - 'link_with' : [libcurlutil_static, libshared], - 'dependencies' : [libcurl], + 'sources' : files('imdsd.c'), + 'extract' : files('imds-util.c'), }, libexec_template + { 'name' : 'systemd-imds', 'public' : true, - 'sources' : files( - 'imds-tool.c', - 'imds-util.c' - ), + 'sources' : files('imds-tool.c'), + 'objects' : ['systemd-imdsd'], }, generator_template + { 'name' : 'systemd-imds-generator', - 'sources' : files( - 'imds-generator.c', - 'imds-util.c' - ), + 'sources' : files('imds-generator.c'), + 'objects' : ['systemd-imdsd'], }, ] diff --git a/src/import/meson.build b/src/import/meson.build index 13c90d7937f..63e632a6cd1 100644 --- a/src/import/meson.build +++ b/src/import/meson.build @@ -4,10 +4,6 @@ if conf.get('ENABLE_IMPORTD') != 1 subdir_done() endif -common_deps = [ - libcurl, -] - executables += [ libexec_template + { 'name' : 'systemd-importd', @@ -21,7 +17,7 @@ executables += [ 'import-common.c', 'qcow2-util.c', ), - 'dependencies' : [common_deps, threads], + 'dependencies' : threads, }, libexec_template + { 'name' : 'systemd-pull', @@ -35,10 +31,7 @@ executables += [ 'pull-tar.c', ), 'objects' : ['systemd-importd'], - 'link_with' : [libcurlutil_static, libshared], - 'dependencies' : common_deps + [ - libopenssl, - ], + 'dependencies' : libopenssl, }, libexec_template + { 'name' : 'systemd-import', @@ -49,7 +42,6 @@ executables += [ 'import-tar.c', ), 'objects' : ['systemd-importd'], - 'dependencies' : common_deps, }, libexec_template + { 'name' : 'systemd-import-fs', @@ -58,7 +50,6 @@ executables += [ 'import-fs.c', ), 'objects' : ['systemd-importd'], - 'dependencies' : common_deps, }, libexec_template + { 'name' : 'systemd-export', @@ -69,14 +60,12 @@ executables += [ 'export-raw.c', ), 'objects' : ['systemd-importd'], - 'dependencies' : common_deps, }, executable_template + { 'name' : 'importctl', 'public' : true, 'sources' : files('importctl.c'), 'objects': ['systemd-importd'], - 'dependencies' : common_deps, }, generator_template + { 'name' : 'systemd-import-generator', @@ -89,13 +78,11 @@ executables += [ test_template + { 'sources' : files('test-qcow2.c'), 'objects' : ['systemd-importd'], - 'dependencies' : common_deps, 'type' : 'manual', }, test_template + { 'sources' : files('test-oci-util.c'), 'objects': ['systemd-importd'], - 'dependencies' : common_deps, }, ] diff --git a/src/import/pull-job.c b/src/import/pull-job.c index 385043dda80..3cc9c0d6926 100644 --- a/src/import/pull-job.c +++ b/src/import/pull-job.c @@ -52,7 +52,7 @@ PullJob* pull_job_unref(PullJob *j) { pull_job_close_disk_fd(j); curl_glue_remove_and_free(j->glue, j->curl); - curl_slist_free_all(j->request_header); + sym_curl_slist_free_all(j->request_header); j->compress = compressor_free(j->compress); @@ -164,13 +164,13 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { CURLcode code; int r; - if (curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&j) != CURLE_OK) + if (sym_curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&j) != CURLE_OK) return; if (!j || IN_SET(j->state, PULL_JOB_DONE, PULL_JOB_FAILED)) return; - code = curl_easy_getinfo(curl, CURLINFO_SCHEME, &scheme); + code = sym_curl_easy_getinfo(curl, CURLINFO_SCHEME, &scheme); if (code != CURLE_OK || !scheme) { r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve URL scheme."); goto finish; @@ -197,16 +197,16 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { } if (result != CURLE_OK) { - r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Transfer failed: %s", curl_easy_strerror(result)); + r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Transfer failed: %s", sym_curl_easy_strerror(result)); goto finish; } if (STRCASE_IN_SET(scheme, "HTTP", "HTTPS")) { long status; - code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); + code = sym_curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); if (code != CURLE_OK) { - r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve response code: %s", curl_easy_strerror(code)); + r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve response code: %s", sym_curl_easy_strerror(code)); goto finish; } @@ -236,9 +236,9 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { if (r < 0) goto finish; - code = curl_easy_getinfo(j->curl, CURLINFO_RESPONSE_CODE, &status); + code = sym_curl_easy_getinfo(j->curl, CURLINFO_RESPONSE_CODE, &status); if (code != CURLE_OK) { - r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve response code: %s", curl_easy_strerror(code)); + r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve response code: %s", sym_curl_easy_strerror(code)); goto finish; } @@ -589,9 +589,9 @@ static size_t pull_job_header_callback(void *contents, size_t size, size_t nmemb assert(j->state == PULL_JOB_ANALYZING); - code = curl_easy_getinfo(j->curl, CURLINFO_RESPONSE_CODE, &status); + code = sym_curl_easy_getinfo(j->curl, CURLINFO_RESPONSE_CODE, &status); if (code != CURLE_OK) { - r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve response code: %s", curl_easy_strerror(code)); + r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve response code: %s", sym_curl_easy_strerror(code)); goto fail; } @@ -781,7 +781,7 @@ int pull_job_add_request_header(PullJob *j, const char *hdr) { if (j->request_header) { struct curl_slist *l; - l = curl_slist_append(j->request_header, hdr); + l = sym_curl_slist_append(j->request_header, hdr); if (!l) return -ENOMEM; @@ -824,29 +824,29 @@ int pull_job_begin(PullJob *j) { } if (j->request_header) { - if (curl_easy_setopt(j->curl, CURLOPT_HTTPHEADER, j->request_header) != CURLE_OK) + if (sym_curl_easy_setopt(j->curl, CURLOPT_HTTPHEADER, j->request_header) != CURLE_OK) return -EIO; } - if (curl_easy_setopt(j->curl, CURLOPT_WRITEFUNCTION, pull_job_write_callback) != CURLE_OK) + if (sym_curl_easy_setopt(j->curl, CURLOPT_WRITEFUNCTION, pull_job_write_callback) != CURLE_OK) return -EIO; - if (curl_easy_setopt(j->curl, CURLOPT_WRITEDATA, j) != CURLE_OK) + if (sym_curl_easy_setopt(j->curl, CURLOPT_WRITEDATA, j) != CURLE_OK) return -EIO; - if (curl_easy_setopt(j->curl, CURLOPT_HEADERFUNCTION, pull_job_header_callback) != CURLE_OK) + if (sym_curl_easy_setopt(j->curl, CURLOPT_HEADERFUNCTION, pull_job_header_callback) != CURLE_OK) return -EIO; - if (curl_easy_setopt(j->curl, CURLOPT_HEADERDATA, j) != CURLE_OK) + if (sym_curl_easy_setopt(j->curl, CURLOPT_HEADERDATA, j) != CURLE_OK) return -EIO; - if (curl_easy_setopt(j->curl, CURLOPT_XFERINFOFUNCTION, pull_job_progress_callback) != CURLE_OK) + if (sym_curl_easy_setopt(j->curl, CURLOPT_XFERINFOFUNCTION, pull_job_progress_callback) != CURLE_OK) return -EIO; - if (curl_easy_setopt(j->curl, CURLOPT_XFERINFODATA, j) != CURLE_OK) + if (sym_curl_easy_setopt(j->curl, CURLOPT_XFERINFODATA, j) != CURLE_OK) return -EIO; - if (curl_easy_setopt(j->curl, CURLOPT_NOPROGRESS, 0L) != CURLE_OK) + if (sym_curl_easy_setopt(j->curl, CURLOPT_NOPROGRESS, 0L) != CURLE_OK) return -EIO; r = curl_glue_add(j->glue, j->curl); diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c index 99de0fc93f5..cd24dec34e3 100644 --- a/src/journal-remote/journal-upload.c +++ b/src/journal-remote/journal-upload.c @@ -188,16 +188,16 @@ int start_upload(Uploader *u, _cleanup_(curl_slist_free_allp) struct curl_slist *h = NULL; struct curl_slist *l; - h = curl_slist_append(NULL, "Content-Type: application/vnd.fdo.journal"); + h = sym_curl_slist_append(NULL, "Content-Type: application/vnd.fdo.journal"); if (!h) return log_oom(); - l = curl_slist_append(h, "Transfer-Encoding: chunked"); + l = sym_curl_slist_append(h, "Transfer-Encoding: chunked"); if (!l) return log_oom(); h = l; - l = curl_slist_append(h, "Accept: text/plain"); + l = sym_curl_slist_append(h, "Accept: text/plain"); if (!l) return log_oom(); h = l; @@ -207,7 +207,7 @@ int start_upload(Uploader *u, if (!header) return log_oom(); - l = curl_slist_append(h, header); + l = sym_curl_slist_append(h, header); if (!l) return log_oom(); h = l; @@ -229,7 +229,7 @@ int start_upload(Uploader *u, if (!header) return log_oom(); - l = curl_slist_append(h, header); + l = sym_curl_slist_append(h, header); if (!l) return log_oom(); h = l; @@ -241,7 +241,7 @@ int start_upload(Uploader *u, if (!u->easy) { _cleanup_(curl_easy_cleanupp) CURL *curl = NULL; - curl = curl_easy_init(); + curl = sym_curl_easy_init(); if (!curl) return log_error_errno(SYNTHETIC_ERRNO(ENOSR), "Call to curl_easy_init failed."); @@ -485,8 +485,10 @@ static int setup_uploader(Uploader *u, const char *url, const char *state_file) static void destroy_uploader(Uploader *u) { assert(u); - curl_easy_cleanup(u->easy); - curl_slist_free_all(u->header); + if (sym_curl_easy_cleanup) + sym_curl_easy_cleanup(u->easy); + if (sym_curl_slist_free_all) + sym_curl_slist_free_all(u->header); free(u->answer); free(u->last_cursor); @@ -527,7 +529,7 @@ static int update_content_encoding_header(Uploader *u, const CompressionConfig * /* If Content-Encoding header is not found, append new one. */ if (!found) { - struct curl_slist *l = curl_slist_append(u->header, header); + struct curl_slist *l = sym_curl_slist_append(u->header, header); if (!l) return log_oom(); u->header = l; @@ -543,7 +545,7 @@ static int update_content_encoding_header(Uploader *u, const CompressionConfig * else u->header = TAKE_PTR(l->next); - curl_slist_free_all(l); + sym_curl_slist_free_all(l); update_header = true; break; } @@ -573,7 +575,7 @@ static int parse_accept_encoding_header(Uploader *u) { return update_content_encoding_header(u, NULL); struct curl_header *header; - CURLHcode hcode = curl_easy_header(u->easy, "Accept-Encoding", 0, CURLH_HEADER, -1, &header); + CURLHcode hcode = sym_curl_easy_header(u->easy, "Accept-Encoding", 0, CURLH_HEADER, -1, &header); if (hcode != CURLHE_OK) goto not_found; @@ -622,7 +624,7 @@ static int perform_upload(Uploader *u) { assert(u); u->watchdog_timestamp = now(CLOCK_MONOTONIC); - code = curl_easy_perform(u->easy); + code = sym_curl_easy_perform(u->easy); if (code) { if (u->error[0]) return log_error_errno(SYNTHETIC_ERRNO(EIO), @@ -631,14 +633,14 @@ static int perform_upload(Uploader *u) { else return log_error_errno(SYNTHETIC_ERRNO(EIO), "Upload to %s failed: %s", - u->url, curl_easy_strerror(code)); + u->url, sym_curl_easy_strerror(code)); } - code = curl_easy_getinfo(u->easy, CURLINFO_RESPONSE_CODE, &status); + code = sym_curl_easy_getinfo(u->easy, CURLINFO_RESPONSE_CODE, &status); if (code) return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "Failed to retrieve response code: %s", - curl_easy_strerror(code)); + sym_curl_easy_strerror(code)); if (status >= 300) return log_error_errno(SYNTHETIC_ERRNO(EIO), @@ -922,6 +924,10 @@ static int run(int argc, char **argv) { if (r <= 0) return r; + r = dlopen_curl(); + if (r < 0) + return r; + r = compression_configs_mangle(&arg_compression); if (r < 0) return r; diff --git a/src/journal-remote/meson.build b/src/journal-remote/meson.build index 51261d000d9..c8d97526d37 100644 --- a/src/journal-remote/meson.build +++ b/src/journal-remote/meson.build @@ -65,7 +65,7 @@ executables += [ 'sources' : systemd_journal_upload_sources, 'extract' : systemd_journal_upload_extract_sources, 'objects' : ['systemd-journal-remote'], - 'dependencies' : common_deps + [libcurl], + 'dependencies' : common_deps, }, test_template + { 'sources' : files('test-journal-header-util.c'), diff --git a/src/report/meson.build b/src/report/meson.build index 6e0c231be32..5f05382999e 100644 --- a/src/report/meson.build +++ b/src/report/meson.build @@ -8,8 +8,6 @@ executables += [ 'report.c', 'report-upload.c', ), - 'link_with' : [libcurlutil_static, libshared], - 'dependencies' : [libcurl], }, libexec_template + { diff --git a/src/report/report-upload.c b/src/report/report-upload.c index 1744d0d91dd..e70f8efa343 100644 --- a/src/report/report-upload.c +++ b/src/report/report-upload.c @@ -12,7 +12,6 @@ #if HAVE_LIBCURL #include "curl-util.h" -#include /* Sadly this fails if ordered first. */ #define SERVER_ANSWER_MAX (1*1024*1024u) @@ -86,6 +85,10 @@ int upload_collected(Context *context) { _cleanup_free_ char *json = NULL; int r; + r = dlopen_curl(); + if (r < 0) + return r; + { /* Convert our variant array to a JSON report. * We won't need the JSON structure again, so free it quickly. */ @@ -110,7 +113,7 @@ int upload_collected(Context *context) { if (r < 0) return log_error_errno(r, "Failed to create curl header: %m"); - _cleanup_(curl_easy_cleanupp) CURL *curl = curl_easy_init(); + _cleanup_(curl_easy_cleanupp) CURL *curl = sym_curl_easy_init(); if (!curl) return log_error_errno(SYNTHETIC_ERRNO(ENOSR), "Call to curl_easy_init failed."); @@ -173,18 +176,18 @@ int upload_collected(Context *context) { if (!easy_setopt(curl, LOG_ERR, CURLOPT_POSTFIELDS, json)) return -EXFULL; - CURLcode code = curl_easy_perform(curl); + CURLcode code = sym_curl_easy_perform(curl); if (code != CURLE_OK) return log_error_errno(SYNTHETIC_ERRNO(EIO), "Upload to %s failed: %s", arg_url, - empty_to_null(&error[0]) ?: curl_easy_strerror(code)); + empty_to_null(&error[0]) ?: sym_curl_easy_strerror(code)); long status; - code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); + code = sym_curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); if (code != CURLE_OK) return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "Failed to retrieve response code: %s", - curl_easy_strerror(code)); + sym_curl_easy_strerror(code)); _cleanup_free_ char *ans = iovw_to_cstring(&context->upload_answer); if (!ans) diff --git a/src/shared/curl-util.c b/src/shared/curl-util.c index 405c083afc7..59f9f16ae37 100644 --- a/src/shared/curl-util.c +++ b/src/shared/curl-util.c @@ -1,9 +1,14 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "curl-util.h" + +#if HAVE_LIBCURL + +#include "sd-dlopen.h" #include "sd-event.h" #include "alloc-util.h" -#include "curl-util.h" +#include "dlfcn-util.h" #include "fd-util.h" #include "hashmap.h" #include "log.h" @@ -12,6 +17,62 @@ #include "time-util.h" #include "version.h" +static void *curl_dl = NULL; + +DLSYM_PROTOTYPE(curl_easy_cleanup) = NULL; +DLSYM_PROTOTYPE(curl_easy_getinfo) = NULL; +DLSYM_PROTOTYPE(curl_easy_init) = NULL; +DLSYM_PROTOTYPE(curl_easy_perform) = NULL; +DLSYM_PROTOTYPE(curl_easy_setopt) = NULL; +DLSYM_PROTOTYPE(curl_easy_strerror) = NULL; +#if LIBCURL_VERSION_NUM >= 0x075300 +DLSYM_PROTOTYPE(curl_easy_header) = NULL; +#endif +DLSYM_PROTOTYPE(curl_getdate) = NULL; +DLSYM_PROTOTYPE(curl_multi_add_handle) = NULL; +DLSYM_PROTOTYPE(curl_multi_assign) = NULL; +DLSYM_PROTOTYPE(curl_multi_cleanup) = NULL; +DLSYM_PROTOTYPE(curl_multi_info_read) = NULL; +DLSYM_PROTOTYPE(curl_multi_init) = NULL; +DLSYM_PROTOTYPE(curl_multi_remove_handle) = NULL; +DLSYM_PROTOTYPE(curl_multi_setopt) = NULL; +DLSYM_PROTOTYPE(curl_multi_socket_action) = NULL; +DLSYM_PROTOTYPE(curl_slist_append) = NULL; +DLSYM_PROTOTYPE(curl_slist_free_all) = NULL; + +int dlopen_curl(void) { + SD_ELF_NOTE_DLOPEN( + "curl", + "Support for downloading and uploading files over HTTP", + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + "libcurl.so.4"); + + return dlopen_many_sym_or_warn( + &curl_dl, + "libcurl.so.4", + LOG_DEBUG, + DLSYM_ARG(curl_easy_cleanup), + DLSYM_ARG(curl_easy_getinfo), + DLSYM_ARG(curl_easy_init), + DLSYM_ARG(curl_easy_perform), + DLSYM_ARG(curl_easy_setopt), + DLSYM_ARG(curl_easy_strerror), +#if LIBCURL_VERSION_NUM >= 0x075300 + DLSYM_ARG(curl_easy_header), +#endif + DLSYM_ARG(curl_getdate), + DLSYM_ARG(curl_multi_add_handle), + DLSYM_ARG(curl_multi_assign), + DLSYM_ARG(curl_multi_cleanup), + DLSYM_ARG(curl_multi_info_read), + DLSYM_ARG(curl_multi_init), + DLSYM_ARG(curl_multi_remove_handle), + DLSYM_ARG(curl_multi_setopt), + DLSYM_ARG(curl_multi_socket_action), + DLSYM_ARG(curl_slist_append), + DLSYM_ARG(curl_slist_free_all)); +} + static void curl_glue_check_finished(CurlGlue *g) { int r; @@ -26,7 +87,7 @@ static void curl_glue_check_finished(CurlGlue *g) { CURLMsg *msg; int k = 0; - msg = curl_multi_info_read(g->curl, &k); + msg = sym_curl_multi_info_read(g->curl, &k); if (!msg) return; @@ -52,7 +113,7 @@ static int curl_glue_on_io(sd_event_source *s, int fd, uint32_t revents, void *u else action = 0; - if (curl_multi_socket_action(g->curl, fd, action, &k) != CURLM_OK) + if (sym_curl_multi_socket_action(g->curl, fd, action, &k) != CURLM_OK) return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to propagate IO event."); @@ -105,7 +166,7 @@ static int curl_glue_socket_callback(CURL *curl, curl_socket_t s, int action, vo if (sd_event_add_io(g->event, &io, s, events, curl_glue_on_io, g) < 0) return -1; - if (curl_multi_assign(g->curl, s, io) != CURLM_OK) + if (sym_curl_multi_assign(g->curl, s, io) != CURLM_OK) return -1; (void) sd_event_source_set_description(io, "curl-io"); @@ -127,7 +188,7 @@ static int curl_glue_on_timer(sd_event_source *s, uint64_t usec, void *userdata) assert(s); - if (curl_multi_socket_action(g->curl, CURL_SOCKET_TIMEOUT, 0, &k) != CURLM_OK) + if (sym_curl_multi_socket_action(g->curl, CURL_SOCKET_TIMEOUT, 0, &k) != CURLM_OK) return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to propagate timeout."); @@ -189,7 +250,7 @@ CurlGlue *curl_glue_unref(CurlGlue *g) { return NULL; if (g->curl) - curl_multi_cleanup(g->curl); + sym_curl_multi_cleanup(g->curl); while ((io = hashmap_steal_first(g->ios))) sd_event_source_unref(io); @@ -210,6 +271,10 @@ int curl_glue_new(CurlGlue **glue, sd_event *event) { assert(glue); + r = dlopen_curl(); + if (r < 0) + return r; + if (event) e = sd_event_ref(event); else { @@ -218,7 +283,7 @@ int curl_glue_new(CurlGlue **glue, sd_event *event) { return r; } - c = curl_multi_init(); + c = sym_curl_multi_init(); if (!c) return -ENOMEM; @@ -231,16 +296,16 @@ int curl_glue_new(CurlGlue **glue, sd_event *event) { .curl = TAKE_PTR(c), }; - if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETDATA, g) != CURLM_OK) + if (sym_curl_multi_setopt(g->curl, CURLMOPT_SOCKETDATA, g) != CURLM_OK) return -EINVAL; - if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETFUNCTION, curl_glue_socket_callback) != CURLM_OK) + if (sym_curl_multi_setopt(g->curl, CURLMOPT_SOCKETFUNCTION, curl_glue_socket_callback) != CURLM_OK) return -EINVAL; - if (curl_multi_setopt(g->curl, CURLMOPT_TIMERDATA, g) != CURLM_OK) + if (sym_curl_multi_setopt(g->curl, CURLMOPT_TIMERDATA, g) != CURLM_OK) return -EINVAL; - if (curl_multi_setopt(g->curl, CURLMOPT_TIMERFUNCTION, curl_glue_timer_callback) != CURLM_OK) + if (sym_curl_multi_setopt(g->curl, CURLMOPT_TIMERFUNCTION, curl_glue_timer_callback) != CURLM_OK) return -EINVAL; r = sd_event_add_defer(g->event, &g->defer, curl_glue_on_defer, g); @@ -257,45 +322,50 @@ int curl_glue_new(CurlGlue **glue, sd_event *event) { int curl_glue_make(CURL **ret, const char *url, void *userdata) { _cleanup_(curl_easy_cleanupp) CURL *c = NULL; const char *useragent; + int r; assert(ret); assert(url); - c = curl_easy_init(); + r = dlopen_curl(); + if (r < 0) + return r; + + c = sym_curl_easy_init(); if (!c) return -ENOMEM; if (DEBUG_LOGGING) - (void) curl_easy_setopt(c, CURLOPT_VERBOSE, 1L); + (void) sym_curl_easy_setopt(c, CURLOPT_VERBOSE, 1L); - if (curl_easy_setopt(c, CURLOPT_URL, url) != CURLE_OK) + if (sym_curl_easy_setopt(c, CURLOPT_URL, url) != CURLE_OK) return -EIO; - if (curl_easy_setopt(c, CURLOPT_PRIVATE, userdata) != CURLE_OK) + if (sym_curl_easy_setopt(c, CURLOPT_PRIVATE, userdata) != CURLE_OK) return -EIO; useragent = strjoina(program_invocation_short_name, "/" GIT_VERSION); - if (curl_easy_setopt(c, CURLOPT_USERAGENT, useragent) != CURLE_OK) + if (sym_curl_easy_setopt(c, CURLOPT_USERAGENT, useragent) != CURLE_OK) return -EIO; - if (curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK) + if (sym_curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK) return -EIO; - if (curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1L) != CURLE_OK) + if (sym_curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1L) != CURLE_OK) return -EIO; - if (curl_easy_setopt(c, CURLOPT_LOW_SPEED_TIME, 60L) != CURLE_OK) + if (sym_curl_easy_setopt(c, CURLOPT_LOW_SPEED_TIME, 60L) != CURLE_OK) return -EIO; - if (curl_easy_setopt(c, CURLOPT_LOW_SPEED_LIMIT, 30L) != CURLE_OK) + if (sym_curl_easy_setopt(c, CURLOPT_LOW_SPEED_LIMIT, 30L) != CURLE_OK) return -EIO; #if LIBCURL_VERSION_NUM >= 0x075500 /* libcurl 7.85.0 */ - if (curl_easy_setopt(c, CURLOPT_PROTOCOLS_STR, "HTTP,HTTPS,FILE") != CURLE_OK) + if (sym_curl_easy_setopt(c, CURLOPT_PROTOCOLS_STR, "HTTP,HTTPS,FILE") != CURLE_OK) #else - if (curl_easy_setopt(c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS|CURLPROTO_FILE) != CURLE_OK) + if (sym_curl_easy_setopt(c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS|CURLPROTO_FILE) != CURLE_OK) return -EIO; - if (curl_easy_setopt(c, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS) != CURLE_OK) + if (sym_curl_easy_setopt(c, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS) != CURLE_OK) #endif return -EIO; @@ -307,7 +377,7 @@ int curl_glue_add(CurlGlue *g, CURL *c) { assert(g); assert(c); - if (curl_multi_add_handle(g->curl, c) != CURLM_OK) + if (sym_curl_multi_add_handle(g->curl, c) != CURLM_OK) return -EIO; return 0; @@ -320,9 +390,9 @@ void curl_glue_remove_and_free(CurlGlue *g, CURL *c) { return; if (g->curl) - curl_multi_remove_handle(g->curl, c); + sym_curl_multi_remove_handle(g->curl, c); - curl_easy_cleanup(c); + sym_curl_easy_cleanup(c); } struct curl_slist *curl_slist_new(const char *first, ...) { @@ -332,7 +402,7 @@ struct curl_slist *curl_slist_new(const char *first, ...) { if (!first) return NULL; - l = curl_slist_append(NULL, first); + l = sym_curl_slist_append(NULL, first); if (!l) return NULL; @@ -346,10 +416,10 @@ struct curl_slist *curl_slist_new(const char *first, ...) { if (!i) break; - n = curl_slist_append(l, i); + n = sym_curl_slist_append(l, i); if (!n) { va_end(ap); - curl_slist_free_all(l); + sym_curl_slist_free_all(l); return NULL; } @@ -397,7 +467,7 @@ int curl_parse_http_time(const char *t, usec_t *ret) { assert(t); assert(ret); - time_t v = curl_getdate(t, NULL); + time_t v = sym_curl_getdate(t, NULL); if (v == (time_t) -1) return -EINVAL; @@ -416,7 +486,7 @@ int curl_append_to_header(struct curl_slist **list, char **headers) { assert(list); STRV_FOREACH(h, headers) { - struct curl_slist *l = curl_slist_append(*list, *h); + struct curl_slist *l = sym_curl_slist_append(*list, *h); if (!l) return -ENOMEM; *list = l; @@ -424,3 +494,5 @@ int curl_append_to_header(struct curl_slist **list, char **headers) { return 0; } + +#endif diff --git a/src/shared/curl-util.h b/src/shared/curl-util.h index 03b6ba7d214..4ca3faf6028 100644 --- a/src/shared/curl-util.h +++ b/src/shared/curl-util.h @@ -1,22 +1,44 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include - #include "shared-forward.h" -#define easy_setopt(curl, log_level, opt, value) ({ \ - CURLcode code = curl_easy_setopt(ASSERT_PTR(curl), opt, value); \ - if (code) \ - log_full(log_level, \ - "curl_easy_setopt %s failed: %s", \ - #opt, curl_easy_strerror(code)); \ - code == CURLE_OK; \ -}) +#if HAVE_LIBCURL +#include /* IWYU pragma: export */ + +#include "dlfcn-util.h" + +extern DLSYM_PROTOTYPE(curl_easy_cleanup); +extern DLSYM_PROTOTYPE(curl_easy_getinfo); +extern DLSYM_PROTOTYPE(curl_easy_init); +extern DLSYM_PROTOTYPE(curl_easy_perform); +extern DLSYM_PROTOTYPE(curl_easy_setopt); +extern DLSYM_PROTOTYPE(curl_easy_strerror); +#if LIBCURL_VERSION_NUM >= 0x075300 +extern DLSYM_PROTOTYPE(curl_easy_header); +#endif +extern DLSYM_PROTOTYPE(curl_getdate); +extern DLSYM_PROTOTYPE(curl_multi_add_handle); +extern DLSYM_PROTOTYPE(curl_multi_assign); +extern DLSYM_PROTOTYPE(curl_multi_cleanup); +extern DLSYM_PROTOTYPE(curl_multi_info_read); +extern DLSYM_PROTOTYPE(curl_multi_init); +extern DLSYM_PROTOTYPE(curl_multi_remove_handle); +extern DLSYM_PROTOTYPE(curl_multi_setopt); +extern DLSYM_PROTOTYPE(curl_multi_socket_action); +extern DLSYM_PROTOTYPE(curl_slist_append); +extern DLSYM_PROTOTYPE(curl_slist_free_all); + +int dlopen_curl(void); -DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(CURL*, curl_easy_cleanup, NULL); -DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(CURLM*, curl_multi_cleanup, NULL); -DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct curl_slist*, curl_slist_free_all, NULL); +#define easy_setopt(curl, log_level, opt, value) ({ \ + CURLcode code = sym_curl_easy_setopt(ASSERT_PTR(curl), opt, value); \ + if (code) \ + log_full(log_level, \ + "curl_easy_setopt %s failed: %s", \ + #opt, sym_curl_easy_strerror(code)); \ + code == CURLE_OK; \ +}) typedef struct CurlGlue CurlGlue; @@ -44,3 +66,15 @@ struct curl_slist *curl_slist_new(const char *first, ...) _sentinel_; int curl_header_strdup(const void *contents, size_t sz, const char *field, char **value); int curl_parse_http_time(const char *t, usec_t *ret); int curl_append_to_header(struct curl_slist **list, char **headers); + +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(CURL*, sym_curl_easy_cleanup, curl_easy_cleanupp, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(CURLM*, sym_curl_multi_cleanup, curl_multi_cleanupp, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(struct curl_slist*, sym_curl_slist_free_all, curl_slist_free_allp, NULL); + +#else + +static inline int dlopen_curl(void) { + return -EOPNOTSUPP; +} + +#endif diff --git a/src/shared/meson.build b/src/shared/meson.build index d99f0f24838..0e45584c6dd 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -53,6 +53,7 @@ shared_sources = files( 'cryptsetup-fido2.c', 'cryptsetup-tpm2.c', 'cryptsetup-util.c', + 'curl-util.c', 'daemon-util.c', 'data-fd-util.c', 'dev-setup.c', @@ -396,6 +397,7 @@ libshared_deps = [threads, libbpf_cflags, libcrypt_cflags, libcryptsetup_cflags, + libcurl_cflags, libdl, libdw_cflags, libelf_cflags, @@ -459,18 +461,3 @@ libshared_fdisk = static_library( userspace], c_args : ['-fvisibility=default'], build_by_default : false) - -# A small shared file that is linked into a few places. -# It is not part of libshared because this code needs libcurl and -# we don't want to link libshared to libcurl. -if conf.get('HAVE_LIBCURL') == 1 - libcurlutil_static = static_library( - 'curl-util', - 'curl-util.c', - implicit_include_directories : false, - dependencies : [userspace, libcurl], - include_directories : includes, - build_by_default : false) -else - libcurlutil_static = [] -endif diff --git a/src/test/test-dlopen-so.c b/src/test/test-dlopen-so.c index 89d21126305..a1f9212e378 100644 --- a/src/test/test-dlopen-so.c +++ b/src/test/test-dlopen-so.c @@ -6,6 +6,7 @@ #include "bpf-dlopen.h" #include "compress.h" #include "cryptsetup-util.h" +#include "curl-util.h" #include "elf-util.h" #include "gcrypt-util.h" #include "idn-util.h" @@ -45,6 +46,7 @@ static int run(int argc, char **argv) { ASSERT_DLOPEN(dlopen_bzip2, HAVE_BZIP2); ASSERT_DLOPEN(dlopen_bpf, HAVE_LIBBPF); ASSERT_DLOPEN(dlopen_cryptsetup, HAVE_LIBCRYPTSETUP); + ASSERT_DLOPEN(dlopen_curl, HAVE_LIBCURL); ASSERT_DLOPEN(dlopen_dw, HAVE_ELFUTILS); ASSERT_DLOPEN(dlopen_elf, HAVE_ELFUTILS); ASSERT_DLOPEN(dlopen_gcrypt, HAVE_GCRYPT);