File: | /shared/playproj/i2c/src/libmain/api_base.c |
Warning: | line 700, column 11 Access to field 'flags' results in a dereference of a null pointer (loaded from variable 'parsed_cmd') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /** @file api_base.c | |||
2 | * | |||
3 | * C API base functions. | |||
4 | */ | |||
5 | ||||
6 | // Copyright (C) 2015-2023 Sanford Rockowitz <rockowitz@minsoft.com> | |||
7 | // SPDX-License-Identifier: GPL-2.0-or-later | |||
8 | ||||
9 | #include "config.h" | |||
10 | ||||
11 | #define _GNU_SOURCE1 1 | |||
12 | #include <assert.h> | |||
13 | #include <dlfcn.h> // _GNU_SOURCE for dladdr() | |||
14 | #include <errno(*__errno_location ()).h> | |||
15 | #include <glib-2.0/glib.h> | |||
16 | #include <signal.h> | |||
17 | #include <string.h> | |||
18 | #include <syslog.h> | |||
19 | ||||
20 | #include "public/ddcutil_c_api.h" | |||
21 | ||||
22 | #include "util/ddcutil_config_file.h" | |||
23 | #include "util/debug_util.h" | |||
24 | #include "util/file_util.h" | |||
25 | #include "util/report_util.h" | |||
26 | #include "util/sysfs_filter_functions.h" | |||
27 | #include "util/xdg_util.h" | |||
28 | ||||
29 | #include "base/base_services.h" | |||
30 | #include "base/build_info.h" | |||
31 | #include "base/core_per_thread_settings.h" | |||
32 | #include "base/core.h" | |||
33 | #include "base/dsa2.h" | |||
34 | #include "base/parms.h" | |||
35 | #include "base/per_display_data.h" | |||
36 | #include "base/per_thread_data.h" | |||
37 | #include "base/rtti.h" | |||
38 | #include "base/trace_control.h" | |||
39 | #include "base/tuned_sleep.h" | |||
40 | ||||
41 | #include "cmdline/cmd_parser.h" | |||
42 | #include "cmdline/parsed_cmd.h" | |||
43 | ||||
44 | #include "i2c/i2c_bus_core.h" // for testing watch_devices | |||
45 | #include "i2c/i2c_display_lock.h" | |||
46 | #include "i2c/i2c_execute.h" // for i2c_set_addr() | |||
47 | ||||
48 | #include "ddc/ddc_common_init.h" | |||
49 | #include "ddc/ddc_displays.h" | |||
50 | #include "ddc/ddc_multi_part_io.h" | |||
51 | #include "ddc/ddc_packet_io.h" | |||
52 | #include "ddc/ddc_serialize.h" | |||
53 | #include "ddc/ddc_services.h" | |||
54 | #include "ddc/ddc_try_data.h" | |||
55 | #include "ddc/ddc_vcp.h" | |||
56 | #include "ddc/ddc_watch_displays.h" | |||
57 | ||||
58 | #include "libmain/api_error_info_internal.h" | |||
59 | #include "libmain/api_base_internal.h" | |||
60 | #include "libmain/api_services_internal.h" | |||
61 | ||||
62 | // | |||
63 | // Forward Declarations | |||
64 | // | |||
65 | ||||
66 | void init_api_base(); | |||
67 | ||||
68 | // | |||
69 | // Globals | |||
70 | // | |||
71 | ||||
72 | bool_Bool library_initialized = false0; | |||
73 | bool_Bool library_initialization_failed = false0; | |||
74 | static bool_Bool client_opened_syslog = false0; | |||
75 | static bool_Bool enable_init_msgs = false0; | |||
76 | static FILE * flog = NULL((void*)0); | |||
77 | static DDCA_Stats_Type requested_stats = 0; | |||
78 | static bool_Bool per_display_stats = false0; | |||
79 | static bool_Bool dsa_detail_stats; | |||
80 | ||||
81 | ||||
82 | // | |||
83 | // Precondition Failure | |||
84 | // | |||
85 | ||||
86 | DDCI_Api_Precondition_Failure_Mode api_failure_mode = DDCI_PRECOND_STDERR_RETURN; | |||
87 | ||||
88 | #ifdef UNUSED | |||
89 | static DDCI_Api_Precondition_Failure_Mode | |||
90 | ddci_set_precondition_failure_mode( | |||
91 | DDCI_Api_Precondition_Failure_Mode failure_mode) | |||
92 | { | |||
93 | DDCI_Api_Precondition_Failure_Mode old = api_failure_mode; | |||
94 | api_failure_mode = failure_mode; | |||
95 | return old; | |||
96 | } | |||
97 | ||||
98 | static DDCI_Api_Precondition_Failure_Mode | |||
99 | ddci_get_precondition_failure_mode() | |||
100 | { | |||
101 | return api_failure_mode; | |||
102 | } | |||
103 | #endif | |||
104 | ||||
105 | ||||
106 | // | |||
107 | // Library Build Information | |||
108 | // | |||
109 | ||||
110 | DDCA_Ddcutil_Version_Spec | |||
111 | ddca_ddcutil_version(void) { | |||
112 | static DDCA_Ddcutil_Version_Spec vspec = {255,255,255}; | |||
113 | static bool_Bool vspec_init = false0; | |||
114 | ||||
115 | if (!vspec_init) { | |||
116 | #ifndef NDEBUG | |||
117 | int ct = | |||
118 | #endif | |||
119 | sscanf(get_base_ddcutil_version(), | |||
120 | "%hhu.%hhu.%hhu", &vspec.major, &vspec.minor, &vspec.micro); | |||
121 | #ifndef NDEBUG | |||
122 | assert(ct == 3)((void) sizeof ((ct == 3) ? 1 : 0), __extension__ ({ if (ct == 3) ; else __assert_fail ("ct == 3", "api_base.c", 122, __extension__ __PRETTY_FUNCTION__); })); | |||
123 | #endif | |||
124 | vspec_init = true1; | |||
125 | } | |||
126 | DBGMSG("Returning: %d.%d.%d", vspec.major, vspec.minor, vspec.micro)dbgtrc(DDCA_TRC_ALL, 0x00, __func__, 126, "api_base.c", "Returning: %d.%d.%d" , vspec.major, vspec.minor, vspec.micro); | |||
127 | return vspec; | |||
128 | } | |||
129 | ||||
130 | ||||
131 | /** Returns the ddcutil version as a string in the form "major.minor.micro". | |||
132 | * | |||
133 | */ | |||
134 | const char * | |||
135 | ddca_ddcutil_version_string(void) { | |||
136 | return get_base_ddcutil_version(); | |||
137 | } | |||
138 | ||||
139 | ||||
140 | // Returns the full ddcutil version as a string that may be suffixed with an extension | |||
141 | const char * | |||
142 | ddca_ddcutil_extended_version_string(void) { | |||
143 | return get_full_ddcutil_version(); | |||
144 | } | |||
145 | ||||
146 | ||||
147 | #ifdef UNUSED | |||
148 | // Indicates whether the ddcutil library was built with support for USB connected monitors. | |||
149 | bool_Bool | |||
150 | ddca_built_with_usb(void) { | |||
151 | #ifdef ENABLE_USB1 | |||
152 | return true1; | |||
153 | #else | |||
154 | return false0; | |||
155 | #endif | |||
156 | } | |||
157 | #endif | |||
158 | ||||
159 | // Alternative to individual ddca_built_with...() functions. | |||
160 | // conciseness vs documentability | |||
161 | // how to document bits? should doxygen doc be in header instead? | |||
162 | ||||
163 | DDCA_Build_Option_Flags | |||
164 | ddca_build_options(void) { | |||
165 | uint8_t result = 0x00; | |||
166 | #ifdef ENABLE_USB1 | |||
167 | result |= DDCA_BUILT_WITH_USB; | |||
168 | #endif | |||
169 | #ifdef FAILSIM_ENABLED | |||
170 | result |= DDCA_BUILT_WITH_FAILSIM; | |||
171 | #endif | |||
172 | // DBGMSG("Returning 0x%02x", result); | |||
173 | return result; | |||
174 | } | |||
175 | ||||
176 | ||||
177 | const char * | |||
178 | ddca_libddcutil_filename(void) { | |||
179 | Dl_info info = {NULL((void*)0),NULL((void*)0),NULL((void*)0),NULL((void*)0)}; | |||
180 | static char fullname[PATH_MAX4096]; | |||
181 | static char * p = NULL((void*)0); | |||
182 | if (!p) { | |||
183 | dladdr(ddca_build_options, &info); | |||
184 | p = realpath(info.dli_fname, fullname); | |||
185 | assert(p == fullname)((void) sizeof ((p == fullname) ? 1 : 0), __extension__ ({ if (p == fullname) ; else __assert_fail ("p == fullname", "api_base.c" , 185, __extension__ __PRETTY_FUNCTION__); })); | |||
186 | } | |||
187 | return p; | |||
188 | } | |||
189 | ||||
190 | ||||
191 | Error_Info* perform_parse( | |||
192 | int new_argc, | |||
193 | char ** new_argv, | |||
194 | char * combined, | |||
195 | Parsed_Cmd ** parsed_cmd_loc) | |||
196 | { | |||
197 | GPtrArray * errmsgs = g_ptr_array_new_with_free_func(g_free); | |||
198 | bool_Bool debug = false0; | |||
199 | ||||
200 | Error_Info * result = NULL((void*)0); | |||
201 | DBGF(debug, "Calling parse_command(), errmsgs=%p\n", errmsgs)do { if (debug) simple_dbgmsg(debug, __func__, 201, "api_base.c" , "Calling parse_command(), errmsgs=%p\n", errmsgs); } while( 0); | |||
202 | *parsed_cmd_loc = parse_command(new_argc, new_argv, MODE_LIBDDCUTIL, errmsgs); | |||
203 | DBGF(debug, "*parsed_cmd_loc=%p, errmsgs->len=%d", *parsed_cmd_loc, errmsgs->len)do { if (debug) simple_dbgmsg(debug, __func__, 203, "api_base.c" , "*parsed_cmd_loc=%p, errmsgs->len=%d", *parsed_cmd_loc, errmsgs ->len); } while(0); | |||
204 | ASSERT_IFF(*parsed_cmd_loc, errmsgs->len == 0)((void) sizeof ((( (*parsed_cmd_loc) && (errmsgs-> len == 0) ) || ( !(*parsed_cmd_loc) && !(errmsgs-> len == 0) )) ? 1 : 0), __extension__ ({ if (( (*parsed_cmd_loc ) && (errmsgs->len == 0) ) || ( !(*parsed_cmd_loc) && !(errmsgs->len == 0) )) ; else __assert_fail ( "( (*parsed_cmd_loc) && (errmsgs->len == 0) ) || ( !(*parsed_cmd_loc) && !(errmsgs->len == 0) )" , "api_base.c", 204, __extension__ __PRETTY_FUNCTION__); })); | |||
205 | if (!*parsed_cmd_loc) { | |||
206 | if (test_emit_syslog(DDCA_SYSLOG_ERROR)) { | |||
207 | syslog(LOG_ERR3, "Invalid option string: %s", combined); | |||
208 | for (int ndx = 0; ndx < errmsgs->len; ndx++) { | |||
209 | char * msg = g_ptr_array_index(errmsgs,ndx)((errmsgs)->pdata)[ndx]; | |||
210 | syslog(LOG_ERR3, "%s", msg); | |||
211 | } | |||
212 | } | |||
213 | result = ERRINFO_NEW(DDCRC_INVALID_CONFIG_FILE, "Invalid option string: %s", combined)errinfo_new((-(3000 +28) ), __func__, "Invalid option string: %s" , combined); | |||
214 | for (int ndx = 0; ndx < errmsgs->len; ndx++) { | |||
215 | char * msg = g_ptr_array_index(errmsgs, ndx)((errmsgs)->pdata)[ndx]; | |||
216 | errinfo_add_cause(result, errinfo_new(DDCRC_INVALID_CONFIG_FILE(-(3000 +28) ), __func__, msg)); | |||
217 | } | |||
218 | } | |||
219 | else { | |||
220 | if (debug) | |||
221 | dbgrpt_parsed_cmd(*parsed_cmd_loc, 1); | |||
222 | } | |||
223 | g_ptr_array_free(errmsgs, true1); | |||
224 | ASSERT_IFF(*parsed_cmd_loc, !result)((void) sizeof ((( (*parsed_cmd_loc) && (!result) ) || ( !(*parsed_cmd_loc) && !(!result) )) ? 1 : 0), __extension__ ({ if (( (*parsed_cmd_loc) && (!result) ) || ( !(*parsed_cmd_loc ) && !(!result) )) ; else __assert_fail ("( (*parsed_cmd_loc) && (!result) ) || ( !(*parsed_cmd_loc) && !(!result) )" , "api_base.c", 224, __extension__ __PRETTY_FUNCTION__); })); | |||
225 | return result; | |||
226 | ||||
227 | } | |||
228 | ||||
229 | ||||
230 | static inline void emit_parse_info_msg(const char * msg, GPtrArray* infomsgs) { | |||
231 | if (infomsgs) | |||
232 | g_ptr_array_add(infomsgs, g_strdup_printf("%s%s", "libddcutil: ", msg)); | |||
233 | SYSLOG2(DDCA_SYSLOG_NOTICE,"%s", msg)do { if (test_emit_syslog(DDCA_SYSLOG_NOTICE)) { int syslog_priority = syslog_importance_from_ddcutil_syslog_level(DDCA_SYSLOG_NOTICE ); if (syslog_priority >= 0) { syslog(syslog_priority, "%s" , msg); } } } while(0); | |||
234 | } | |||
235 | ||||
236 | ||||
237 | // | |||
238 | // Initialization | |||
239 | // | |||
240 | static Error_Info * | |||
241 | get_parsed_libmain_config(const char * libopts_string, | |||
242 | bool_Bool disable_config_file, | |||
243 | GPtrArray* infomsgs, | |||
244 | Parsed_Cmd** parsed_cmd_loc) | |||
245 | { | |||
246 | bool_Bool debug = false0; | |||
247 | DBGF(debug, "Starting. disable_config_file = %s, libopts_string = %sn",do { if (debug) simple_dbgmsg(debug, __func__, 248, "api_base.c" , "Starting. disable_config_file = %s, libopts_string = %sn", sbool(disable_config_file), libopts_string); } while(0) | |||
248 | sbool(disable_config_file), libopts_string)do { if (debug) simple_dbgmsg(debug, __func__, 248, "api_base.c" , "Starting. disable_config_file = %s, libopts_string = %sn", sbool(disable_config_file), libopts_string); } while(0); | |||
249 | ||||
250 | char * msg = g_strdup_printf("Options passed from client: %s", | |||
251 | (libopts_string) ? libopts_string : ""); | |||
252 | emit_parse_info_msg(msg, infomsgs); | |||
253 | free(msg); | |||
254 | ||||
255 | Error_Info * result = NULL((void*)0); | |||
256 | *parsed_cmd_loc = NULL((void*)0); | |||
257 | ||||
258 | char ** libopts_tokens = NULL((void*)0); | |||
259 | int libopts_token_ct = 0; | |||
260 | if (libopts_string) { | |||
261 | libopts_token_ct = tokenize_options_line(libopts_string, &libopts_tokens); | |||
262 | DBGF(debug, "libopts_token_ct = %d, libopts_tokens=%p:", libopts_token_ct,libopts_tokens)do { if (debug) simple_dbgmsg(debug, __func__, 262, "api_base.c" , "libopts_token_ct = %d, libopts_tokens=%p:", libopts_token_ct ,libopts_tokens); } while(0); | |||
263 | if (debug) | |||
264 | ntsa_show(libopts_tokens); | |||
265 | } | |||
266 | Null_Terminated_String_Array cmd_name_array = calloc(2 + libopts_token_ct, sizeof(char*)); | |||
267 | cmd_name_array[0] = strdup("libddcutil"); // so libddcutil not a special case for parser | |||
268 | int ndx = 0; | |||
269 | for (; ndx < libopts_token_ct; ndx++) | |||
270 | cmd_name_array[ndx+1] = g_strdup(libopts_tokens[ndx])g_strdup_inline (libopts_tokens[ndx]); | |||
271 | cmd_name_array[ndx+1] = NULL((void*)0); | |||
272 | ntsa_free(libopts_tokens,true1); | |||
273 | ||||
274 | DBGF(debug, "cmd_name_array=%p, cmd_name_array[1]=%p -> %s",do { if (debug) simple_dbgmsg(debug, __func__, 275, "api_base.c" , "cmd_name_array=%p, cmd_name_array[1]=%p -> %s", cmd_name_array , cmd_name_array[0], cmd_name_array[0]); } while(0) | |||
275 | cmd_name_array, cmd_name_array[0], cmd_name_array[0])do { if (debug) simple_dbgmsg(debug, __func__, 275, "api_base.c" , "cmd_name_array=%p, cmd_name_array[1]=%p -> %s", cmd_name_array , cmd_name_array[0], cmd_name_array[0]); } while(0); | |||
276 | ||||
277 | char ** new_argv = NULL((void*)0); | |||
278 | int new_argc = 0; | |||
279 | char * untokenized_option_string = NULL((void*)0); | |||
280 | ||||
281 | if (disable_config_file) { | |||
282 | DBGF(debug, "config file disabled")do { if (debug) simple_dbgmsg(debug, __func__, 282, "api_base.c" , "config file disabled"); } while(0); | |||
283 | new_argv = ntsa_copy(cmd_name_array, true1); | |||
284 | new_argc = ntsa_length(cmd_name_array); | |||
285 | ntsa_free(cmd_name_array, true1); | |||
286 | } | |||
287 | else { | |||
288 | GPtrArray * errmsgs = g_ptr_array_new_with_free_func(g_free); | |||
289 | char * config_fn = NULL((void*)0); | |||
290 | DBGF(debug, "Calling apply_config_file()...")do { if (debug) simple_dbgmsg(debug, __func__, 290, "api_base.c" , "Calling apply_config_file()..."); } while(0); | |||
291 | int apply_config_rc = apply_config_file( | |||
292 | "libddcutil", // use this section of config file | |||
293 | ntsa_length(cmd_name_array), cmd_name_array, | |||
294 | &new_argc, | |||
295 | &new_argv, | |||
296 | &untokenized_option_string, | |||
297 | &config_fn, | |||
298 | errmsgs); | |||
299 | ntsa_free(cmd_name_array, true1); | |||
300 | assert(apply_config_rc <= 0)((void) sizeof ((apply_config_rc <= 0) ? 1 : 0), __extension__ ({ if (apply_config_rc <= 0) ; else __assert_fail ("apply_config_rc <= 0" , "api_base.c", 300, __extension__ __PRETTY_FUNCTION__); })); | |||
301 | ASSERT_IFF(apply_config_rc == 0, errmsgs->len == 0)((void) sizeof ((( (apply_config_rc == 0) && (errmsgs ->len == 0) ) || ( !(apply_config_rc == 0) && !(errmsgs ->len == 0) )) ? 1 : 0), __extension__ ({ if (( (apply_config_rc == 0) && (errmsgs->len == 0) ) || ( !(apply_config_rc == 0) && !(errmsgs->len == 0) )) ; else __assert_fail ("( (apply_config_rc == 0) && (errmsgs->len == 0) ) || ( !(apply_config_rc == 0) && !(errmsgs->len == 0) )" , "api_base.c", 301, __extension__ __PRETTY_FUNCTION__); })); | |||
302 | // DBGF(debug, "Calling ntsa_free(cmd_name_array=%p", cmd_name_array); | |||
303 | ||||
304 | DBGF(debug, "apply_config_file() returned: %d (%s), new_argc=%d, new_argv=%p:",do { if (debug) simple_dbgmsg(debug, __func__, 305, "api_base.c" , "apply_config_file() returned: %d (%s), new_argc=%d, new_argv=%p:" , apply_config_rc, psc_desc(apply_config_rc), new_argc, new_argv ); } while(0) | |||
305 | apply_config_rc, psc_desc(apply_config_rc), new_argc, new_argv)do { if (debug) simple_dbgmsg(debug, __func__, 305, "api_base.c" , "apply_config_file() returned: %d (%s), new_argc=%d, new_argv=%p:" , apply_config_rc, psc_desc(apply_config_rc), new_argc, new_argv ); } while(0); | |||
306 | ||||
307 | if (apply_config_rc == -EBADMSG74) { | |||
308 | result = errinfo_new(DDCRC_INVALID_CONFIG_FILE(-(3000 +28) ), __func__, | |||
309 | "Error(s) processing configuration file: %s", config_fn); | |||
310 | for (int ndx = 0; ndx < errmsgs->len; ndx++) { | |||
311 | errinfo_add_cause(result, | |||
312 | errinfo_new(DDCRC_INVALID_CONFIG_FILE(-(3000 +28) ), __func__, g_ptr_array_index(errmsgs, ndx)((errmsgs)->pdata)[ndx])); | |||
313 | } | |||
314 | ||||
315 | } | |||
316 | // else if (apply_config_rc == -ENOENT) { | |||
317 | // result = errinfo_new(-ENOENT, __func__, "Configuration file not found"); | |||
318 | // } | |||
319 | else if (apply_config_rc < 0) { | |||
320 | result = errinfo_new(apply_config_rc, __func__, | |||
321 | "Unexpected error reading configuration file: %s", psc_desc(apply_config_rc)); | |||
322 | } | |||
323 | else { | |||
324 | assert( new_argc == ntsa_length(new_argv) )((void) sizeof ((new_argc == ntsa_length(new_argv)) ? 1 : 0), __extension__ ({ if (new_argc == ntsa_length(new_argv)) ; else __assert_fail ("new_argc == ntsa_length(new_argv)", "api_base.c" , 324, __extension__ __PRETTY_FUNCTION__); })); | |||
325 | if (debug) | |||
326 | ntsa_show(new_argv); | |||
327 | ||||
328 | if (untokenized_option_string && strlen(untokenized_option_string) > 0) { | |||
329 | char * msg = g_strdup_printf("Using options from %s: %s", | |||
330 | config_fn, untokenized_option_string); | |||
331 | emit_parse_info_msg(msg, infomsgs); | |||
332 | free(msg); | |||
333 | } | |||
334 | } | |||
335 | g_ptr_array_free(errmsgs, true1); | |||
336 | free(config_fn); | |||
337 | } | |||
338 | ||||
339 | if (!result) { // if no errors | |||
340 | assert(new_argc >= 1)((void) sizeof ((new_argc >= 1) ? 1 : 0), __extension__ ({ if (new_argc >= 1) ; else __assert_fail ("new_argc >= 1" , "api_base.c", 340, __extension__ __PRETTY_FUNCTION__); })); | |||
341 | char * combined = strjoin((const char**)(new_argv+1), new_argc, " "); | |||
342 | char * msg = g_strdup_printf("Applying combined options: %s", combined); | |||
343 | emit_parse_info_msg(msg, infomsgs); | |||
344 | free(msg); | |||
345 | ||||
346 | result = perform_parse(new_argc, new_argv, combined, parsed_cmd_loc); | |||
347 | ntsa_free(new_argv, true1); | |||
348 | free(combined); | |||
349 | free(untokenized_option_string); | |||
350 | } | |||
351 | ||||
352 | DBGF(debug, "Done. *parsed_cmd_loc=%p. Returning %s",do { if (debug) simple_dbgmsg(debug, __func__, 353, "api_base.c" , "Done. *parsed_cmd_loc=%p. Returning %s", *parsed_cmd_loc , errinfo_summary(result)); } while(0) | |||
353 | *parsed_cmd_loc, errinfo_summary(result))do { if (debug) simple_dbgmsg(debug, __func__, 353, "api_base.c" , "Done. *parsed_cmd_loc=%p. Returning %s", *parsed_cmd_loc , errinfo_summary(result)); } while(0); | |||
354 | ||||
355 | ASSERT_IFF(*parsed_cmd_loc, !result)((void) sizeof ((( (*parsed_cmd_loc) && (!result) ) || ( !(*parsed_cmd_loc) && !(!result) )) ? 1 : 0), __extension__ ({ if (( (*parsed_cmd_loc) && (!result) ) || ( !(*parsed_cmd_loc ) && !(!result) )) ; else __assert_fail ("( (*parsed_cmd_loc) && (!result) ) || ( !(*parsed_cmd_loc) && !(!result) )" , "api_base.c", 355, __extension__ __PRETTY_FUNCTION__); })); | |||
356 | return result; | |||
357 | } | |||
358 | ||||
359 | ||||
360 | #ifdef TESTING_CLEANUP | |||
361 | void done() { | |||
362 | printf("(%s) Starting\n", __func__); | |||
363 | _ddca_terminate(); | |||
364 | SYSLOG(LOG_INFO6, "(%s) executing done()", __func__); | |||
365 | printf("(%s) Done.\n", __func__); | |||
366 | } | |||
367 | ||||
368 | void dummy_sigterm_handler() { | |||
369 | printf("(%s) Executing. library_initialized = %s\n", | |||
370 | __func__, SBOOL(library_initialized)( (library_initialized) ? "true" : "false" )); | |||
371 | } | |||
372 | ||||
373 | void atexit_func() { | |||
374 | printf("(%s) Executing. library_initalized = %s\n", | |||
375 | __func__, SBOOL(library_initialized)( (library_initialized) ? "true" : "false" )); | |||
376 | } | |||
377 | #endif | |||
378 | ||||
379 | ||||
380 | ||||
381 | /** Initializes the ddcutil library module. | |||
382 | * | |||
383 | * Called automatically when the shared library is loaded. | |||
384 | * | |||
385 | * Registers functions in RTTI table, performs additional initialization | |||
386 | * that cannot fail. | |||
387 | */ | |||
388 | void __attribute__ ((constructor)) | |||
389 | _ddca_new_init(void) { | |||
390 | bool_Bool debug = false0; | |||
391 | char * s = getenv("DDCUTIL_DEBUG_LIBINIT"); | |||
392 | if (s && strlen(s) > 0) | |||
393 | debug = true1; | |||
394 | ||||
395 | DBGF(debug, "Starting. library_initialized=%s\n", sbool(library_initialized))do { if (debug) simple_dbgmsg(debug, __func__, 395, "api_base.c" , "Starting. library_initialized=%s\n", sbool(library_initialized )); } while(0); | |||
396 | ||||
397 | init_api_base(); // registers functions in RTTI table | |||
398 | init_base_services(); // initializes tracing related modules | |||
399 | init_ddc_services(); // initializes i2c, usb, ddc, vcp, dynvcp | |||
400 | init_api_services(); // other files in directory libmain | |||
401 | ||||
402 | #ifdef TESTING_CLEANUP | |||
403 | // int atexit_rc = atexit(done); // TESTING CLEANUP | |||
404 | // printf("(%s) atexit() returned %d\n", __func__, atexit_rc); | |||
405 | #endif | |||
406 | ||||
407 | DBGF(debug, "Done.")do { if (debug) simple_dbgmsg(debug, __func__, 407, "api_base.c" , "Done."); } while(0); | |||
408 | } | |||
409 | ||||
410 | ||||
411 | // | |||
412 | // Profiling | |||
413 | // | |||
414 | ||||
415 | void profiling_enable(bool_Bool enabled) { | |||
416 | ptd_api_profiling_enabled = enabled; | |||
417 | } | |||
418 | ||||
419 | void profiling_reset() { | |||
420 | ptd_profile_reset_all_stats(); | |||
421 | } | |||
422 | ||||
423 | void profile_start_call(void * func) { | |||
424 | ptd_profile_function_start(func); | |||
425 | } | |||
426 | ||||
427 | void profile_end_call(void * func) { | |||
428 | ptd_profile_function_end(func); | |||
429 | } | |||
430 | ||||
431 | void profile_report(FILE * dest, bool_Bool by_thread) { | |||
432 | if (dest) { | |||
433 | rpt_push_output_dest(dest); | |||
434 | } | |||
435 | ptd_profile_report_all_threads(0); | |||
436 | ptd_profile_report_stats_summary(0); | |||
437 | if (dest) { | |||
438 | rpt_pop_output_dest(); | |||
439 | } | |||
440 | } | |||
441 | ||||
442 | ||||
443 | // | |||
444 | // Tracing | |||
445 | // | |||
446 | ||||
447 | void | |||
448 | init_library_trace_file(char * library_trace_file, bool_Bool enable_syslog, bool_Bool debug) { | |||
449 | DBGF(debug, "library_trace_file = \"%s\", enable_syslog = %s", library_trace_file, sbool(enable_syslog))do { if (debug) simple_dbgmsg(debug, __func__, 449, "api_base.c" , "library_trace_file = \"%s\", enable_syslog = %s", library_trace_file , sbool(enable_syslog)); } while(0); | |||
450 | char * trace_file = (library_trace_file[0] != '/') | |||
451 | ? xdg_state_home_file("ddcutil", library_trace_file) | |||
452 | : g_strdup(library_trace_file)g_strdup_inline (library_trace_file); | |||
453 | DBGF(debug, "Setting trace destination %s", trace_file)do { if (debug) simple_dbgmsg(debug, __func__, 453, "api_base.c" , "Setting trace destination %s", trace_file); } while(0); | |||
454 | SYSLOG2(DDCA_SYSLOG_NOTICE, "Trace destination: %s", trace_file)do { if (test_emit_syslog(DDCA_SYSLOG_NOTICE)) { int syslog_priority = syslog_importance_from_ddcutil_syslog_level(DDCA_SYSLOG_NOTICE ); if (syslog_priority >= 0) { syslog(syslog_priority, "Trace destination: %s" , trace_file); } } } while(0); | |||
455 | ||||
456 | fopen_mkdir(trace_file, "a", stderrstderr, &flog); | |||
457 | if (flog) { | |||
458 | time_t trace_start_time = time(NULL((void*)0)); | |||
459 | char * trace_start_time_s = asctime(localtime(&trace_start_time)); | |||
460 | if (trace_start_time_s[strlen(trace_start_time_s)-1] == 0x0a) | |||
461 | trace_start_time_s[strlen(trace_start_time_s)-1] = 0; | |||
462 | fprintf(flog, "%s tracing started %s\n", "libddcutil", trace_start_time_s); | |||
463 | DBGF(debug, "Writing %s trace output to %s", "libddcutil",trace_file)do { if (debug) simple_dbgmsg(debug, __func__, 463, "api_base.c" , "Writing %s trace output to %s", "libddcutil",trace_file); } while(0); | |||
464 | set_default_thread_output_settings(flog, flog); | |||
465 | set_fout(flog); | |||
466 | set_ferr(flog); | |||
467 | ||||
468 | rpt_set_default_output_dest(flog); // for future threads | |||
469 | rpt_push_output_dest(flog); // for this thread | |||
470 | } | |||
471 | else { | |||
472 | fprintf(stderrstderr, "Error opening libddcutil trace file %s: %s\n", | |||
473 | trace_file, strerror(errno(*__errno_location ()))); | |||
474 | SYSLOG2(DDCA_SYSLOG_ERROR, "Error opening libddcutil trace file %s: %s",do { if (test_emit_syslog(DDCA_SYSLOG_ERROR)) { int syslog_priority = syslog_importance_from_ddcutil_syslog_level(DDCA_SYSLOG_ERROR ); if (syslog_priority >= 0) { syslog(syslog_priority, "Error opening libddcutil trace file %s: %s" , trace_file, strerror((*__errno_location ()))); } } } while( 0) | |||
475 | trace_file, strerror(errno))do { if (test_emit_syslog(DDCA_SYSLOG_ERROR)) { int syslog_priority = syslog_importance_from_ddcutil_syslog_level(DDCA_SYSLOG_ERROR ); if (syslog_priority >= 0) { syslog(syslog_priority, "Error opening libddcutil trace file %s: %s" , trace_file, strerror((*__errno_location ()))); } } } while( 0); | |||
476 | } | |||
477 | free(trace_file); | |||
478 | DBGF(debug, "Done.")do { if (debug) simple_dbgmsg(debug, __func__, 478, "api_base.c" , "Done."); } while(0); | |||
479 | } | |||
480 | ||||
481 | ||||
482 | /** Cleanup at library termination | |||
483 | * | |||
484 | * - Terminates thread that watches for display addition or removal. | |||
485 | * - Releases heap memory to avoid error reports from memory analyzers. | |||
486 | */ | |||
487 | void __attribute__ ((destructor)) | |||
488 | _ddca_terminate(void) { | |||
489 | bool_Bool debug = false0; | |||
490 | DBGTRC_STARTING(debug, DDCA_TRC_API, "library_initialized = %s", SBOOL(library_initialized))dbgtrc( (debug || trace_callstack_call_depth > 0 || is_traced_callstack_call (__func__) ) ? DDCA_TRC_ALL : (DDCA_TRC_API), 0x08, __func__, 490, "api_base.c", "Starting ""library_initialized = %s", ( (library_initialized) ? "true" : "false" )); | |||
491 | if (library_initialized) { | |||
492 | if (debug) | |||
493 | dbgrpt_display_locks(2); | |||
494 | if (dsa2_is_enabled()) | |||
495 | dsa2_save_persistent_stats(); | |||
496 | if (display_caching_enabled) | |||
497 | ddc_store_displays_cache(); | |||
498 | ddc_discard_detected_displays(); | |||
499 | if (requested_stats) | |||
500 | ddc_report_stats_main(requested_stats, per_display_stats, dsa_detail_stats, false0, 0); | |||
501 | ddc_stop_watch_displays(/*wait=*/ false0); // in case it was started | |||
502 | terminate_ddc_services(); | |||
503 | terminate_base_services(); | |||
504 | free_regex_hash_table(); | |||
505 | library_initialized = false0; | |||
506 | if (flog) | |||
507 | fclose(flog); | |||
508 | DBGTRC_DONE(debug, DDCA_TRC_API, "library termination complete")dbgtrc( (debug) || trace_callstack_call_depth > 0 ? DDCA_TRC_ALL : (DDCA_TRC_API), 0x10, __func__, 508, "api_base.c", "Done " "library termination complete"); | |||
509 | } | |||
510 | else { | |||
511 | DBGTRC_DONE(debug, DDCA_TRC_API, "library was already terminated")dbgtrc( (debug) || trace_callstack_call_depth > 0 ? DDCA_TRC_ALL : (DDCA_TRC_API), 0x10, __func__, 511, "api_base.c", "Done " "library was already terminated"); // should be impossible | |||
512 | } | |||
513 | // special handling for termination msg | |||
514 | if (syslog_level > DDCA_SYSLOG_NEVER) | |||
515 | syslog(LOG_NOTICE5, "libddcutil terminating."); | |||
516 | if (syslog_level > DDCA_SYSLOG_NEVER && !client_opened_syslog) | |||
517 | closelog(); | |||
518 | } | |||
519 | ||||
520 | ||||
521 | Error_Info * | |||
522 | set_master_errinfo_from_init_errors( | |||
523 | GPtrArray * errs) // array of Error_Info * | |||
524 | { | |||
525 | bool_Bool debug = false0; | |||
526 | DBGF(debug, "Starting. errs=%p", errs)do { if (debug) simple_dbgmsg(debug, __func__, 526, "api_base.c" , "Starting. errs=%p", errs); } while(0); | |||
527 | Error_Info * master_error = NULL((void*)0); | |||
528 | if (errs && errs->len > 0) { | |||
529 | master_error = errinfo_new(DDCRC_BAD_DATA(-(3000 +27) ), __func__, "Invalid configuration options"); | |||
530 | for (int ndx = 0; ndx < errs->len; ndx++) { | |||
531 | Error_Info * cur = g_ptr_array_index(errs, ndx)((errs)->pdata)[ndx]; | |||
532 | errinfo_add_cause(master_error, cur); | |||
533 | } | |||
534 | g_ptr_array_free(errs, false0); | |||
535 | } | |||
536 | DBGF(debug, "Done. Returning %p")do { if (debug) simple_dbgmsg(debug, __func__, 536, "api_base.c" , "Done. Returning %p"); } while(0); | |||
537 | return master_error; | |||
538 | } | |||
539 | ||||
540 | ||||
541 | DDCA_Status | |||
542 | set_ddca_error_detail_from_init_errors( | |||
543 | GPtrArray * errs) // array of Error_Info * | |||
544 | { | |||
545 | bool_Bool debug = false0; | |||
546 | DDCA_Status ddcrc = 0; | |||
547 | if (errs && errs->len > 0) { | |||
548 | Error_Info * master_error = errinfo_new(DDCRC_BAD_DATA(-(3000 +27) ), __func__, "Invalid configuration options"); | |||
549 | ddcrc = DDCRC_BAD_DATA(-(3000 +27) ); | |||
550 | for (int ndx = 0; ndx < errs->len; ndx++) { | |||
551 | Error_Info * cur = g_ptr_array_index(errs, ndx)((errs)->pdata)[ndx]; | |||
552 | errinfo_add_cause(master_error, cur); | |||
553 | } | |||
554 | DDCA_Error_Detail * public_error_detail = error_info_to_ddca_detail(master_error); | |||
555 | errinfo_free_with_report(master_error, debug, __func__); | |||
556 | save_thread_error_detail(public_error_detail); | |||
557 | } | |||
558 | // clear if no errors? | |||
559 | return ddcrc; | |||
560 | } | |||
561 | ||||
562 | ||||
563 | DDCA_Syslog_Level ddca_syslog_level_from_name(const char * name) { | |||
564 | return syslog_level_name_to_value(name); | |||
565 | } | |||
566 | ||||
567 | ||||
568 | void report_parse_errors0(Error_Info * erec, int depth, int max_depth) { | |||
569 | char * edesc = psc_text(erec->status_code); | |||
570 | ||||
571 | if (depth == 0) { | |||
572 | rpt_vstring(depth, "%s: %s", edesc, erec->detail); | |||
573 | } | |||
574 | else { | |||
575 | rpt_vstring(depth, "%s", erec->detail); | |||
576 | } | |||
577 | if (depth < max_depth) { | |||
578 | if (erec->cause_ct > 0) { | |||
579 | for (int ndx = 0; ndx < erec->cause_ct; ndx++) { | |||
580 | Error_Info * cur = erec->causes[ndx]; | |||
581 | report_parse_errors0(cur, depth+1, max_depth); | |||
582 | } | |||
583 | } | |||
584 | } | |||
585 | } | |||
586 | ||||
587 | ||||
588 | void report_parse_errors(Error_Info * erec) { | |||
589 | if (erec) { | |||
590 | rpt_push_output_dest(ferr()); | |||
591 | report_parse_errors0(erec, 0, 3); | |||
592 | rpt_pop_output_dest(); | |||
593 | } | |||
594 | } | |||
595 | ||||
596 | ||||
597 | DDCA_Status | |||
598 | ddci_init(const char * libopts, | |||
599 | DDCA_Syslog_Level syslog_level_arg, | |||
600 | DDCA_Init_Options opts, | |||
601 | char*** infomsg_loc) | |||
602 | { | |||
603 | bool_Bool debug = false0; | |||
604 | char * s = getenv("DDCUTIL_DEBUG_LIBINIT"); | |||
605 | if (s
| |||
606 | debug = true1; | |||
607 | ||||
608 | DBGF(debug, "Starting. library_initialized=%s", sbool(library_initialized))do { if (debug) simple_dbgmsg(debug, __func__, 608, "api_base.c" , "Starting. library_initialized=%s", sbool(library_initialized )); } while(0); | |||
609 | ||||
610 | if (infomsg_loc
| |||
611 | *infomsg_loc = NULL((void*)0); | |||
612 | ||||
613 | Parsed_Cmd * parsed_cmd = NULL((void*)0); | |||
614 | Error_Info * master_error = NULL((void*)0); | |||
615 | if (library_initialized) { | |||
616 | master_error = ERRINFO_NEW(DDCRC_INVALID_OPERATION, "libddcutil already initialized")errinfo_new((-(3000 +14) ), __func__, "libddcutil already initialized" ); | |||
617 | SYSLOG2(DDCA_SYSLOG_ERROR, "libddcutil already initialized")do { if (test_emit_syslog(DDCA_SYSLOG_ERROR)) { int syslog_priority = syslog_importance_from_ddcutil_syslog_level(DDCA_SYSLOG_ERROR ); if (syslog_priority >= 0) { syslog(syslog_priority, "libddcutil already initialized" ); } } } while(0); | |||
618 | } | |||
619 | else { | |||
620 | enable_init_msgs = opts & DDCA_INIT_OPTIONS_ENABLE_INIT_MSGS; | |||
621 | // enable_init_msgs = true; // *** TEMP *** | |||
622 | client_opened_syslog = opts & DDCA_INIT_OPTIONS_CLIENT_OPENED_SYSLOG; | |||
623 | if (syslog_level_arg == DDCA_SYSLOG_NOT_SET) | |||
624 | syslog_level_arg = DEFAULT_LIBDDCUTIL_SYSLOG_LEVELDDCA_SYSLOG_NOTICE; | |||
625 | if (syslog_level_arg != DDCA_SYSLOG_NEVER) { | |||
626 | enable_syslog = true1; | |||
627 | if (!client_opened_syslog) { | |||
628 | openlog("libddcutil", // prepended to every log message | |||
629 | LOG_CONS0x02 | LOG_PID0x01, // write to system console if error sending to system logger | |||
630 | // include caller's process id | |||
631 | LOG_USER(1<<3)); // generic user program, syslogger can use to determine how to handle | |||
632 | } | |||
633 | // special handling for start and termination msgs | |||
634 | // always output if syslog is opened | |||
635 | syslog(LOG_NOTICE5, "Initializing libddcutil. ddcutil version: %s, shared library: %s", | |||
636 | get_full_ddcutil_version(), ddca_libddcutil_filename()); | |||
637 | } | |||
638 | syslog_level = syslog_level_arg; // global in trace_control.h | |||
639 | ||||
640 | GPtrArray* infomsgs = NULL((void*)0); | |||
641 | infomsgs = g_ptr_array_new_with_free_func(g_free); | |||
642 | ||||
643 | if ((opts & DDCA_INIT_OPTIONS_DISABLE_CONFIG_FILE) && !libopts) { | |||
644 | parsed_cmd = new_parsed_cmd(); | |||
645 | } | |||
646 | else { | |||
647 | master_error = get_parsed_libmain_config( | |||
648 | libopts, | |||
649 | opts & DDCA_INIT_OPTIONS_DISABLE_CONFIG_FILE, | |||
650 | infomsgs, | |||
651 | &parsed_cmd); | |||
652 | ASSERT_IFF(master_error, !parsed_cmd)((void) sizeof ((( (master_error) && (!parsed_cmd) ) || ( !(master_error) && !(!parsed_cmd) )) ? 1 : 0), __extension__ ({ if (( (master_error) && (!parsed_cmd) ) || ( !(master_error ) && !(!parsed_cmd) )) ; else __assert_fail ("( (master_error) && (!parsed_cmd) ) || ( !(master_error) && !(!parsed_cmd) )" , "api_base.c", 652, __extension__ __PRETTY_FUNCTION__); })); | |||
653 | ||||
654 | if (enable_init_msgs && infomsgs && infomsgs->len > 0) { | |||
655 | for (int ndx = 0; ndx < infomsgs->len; ndx++) | |||
656 | fprintf(fout(), "%s\n", (char*) g_ptr_array_index(infomsgs, ndx)((infomsgs)->pdata)[ndx]); | |||
657 | } | |||
658 | if (infomsg_loc) { | |||
659 | *infomsg_loc = g_ptr_array_to_ntsa(infomsgs, /*duplicate=*/true1); | |||
660 | } | |||
661 | g_ptr_array_free(infomsgs, true1); | |||
662 | ||||
663 | if (!master_error) { | |||
664 | if (parsed_cmd->trace_destination) { | |||
665 | DBGF(debug, "Setting library trace file: %s", parsed_cmd->trace_destination)do { if (debug) simple_dbgmsg(debug, __func__, 665, "api_base.c" , "Setting library trace file: %s", parsed_cmd->trace_destination ); } while(0); | |||
666 | init_library_trace_file(parsed_cmd->trace_destination, enable_syslog, debug); | |||
667 | } | |||
668 | master_error = init_tracing(parsed_cmd); | |||
669 | requested_stats = parsed_cmd->stats_types; | |||
670 | ptd_api_profiling_enabled = parsed_cmd->flags & CMD_FLAG_PROFILE_API; | |||
671 | per_display_stats = parsed_cmd->flags & CMD_FLAG_VERBOSE_STATS; | |||
672 | dsa_detail_stats = parsed_cmd->flags & CMD_FLAG_INTERNAL_STATS; | |||
673 | if (!submaster_initializer(parsed_cmd)) | |||
674 | master_error = ERRINFO_NEW(DDCRC_UNINITIALIZED, "Initialization failed")errinfo_new((-(3000 +16) ), __func__, "Initialization failed" ); | |||
675 | } | |||
676 | } | |||
677 | } | |||
678 | ||||
679 | DDCA_Status ddcrc = 0; | |||
680 | if (master_error) { | |||
681 | ddcrc = master_error->status_code; | |||
682 | DDCA_Error_Detail * public_error_detail = error_info_to_ddca_detail(master_error); | |||
683 | save_thread_error_detail(public_error_detail); | |||
684 | if (test_emit_syslog(DDCA_SYSLOG_ERROR)) { | |||
685 | SYSLOG2(DDCA_SYSLOG_ERROR, "Library initialization failed: %s", psc_desc(master_error->status_code))do { if (test_emit_syslog(DDCA_SYSLOG_ERROR)) { int syslog_priority = syslog_importance_from_ddcutil_syslog_level(DDCA_SYSLOG_ERROR ); if (syslog_priority >= 0) { syslog(syslog_priority, "Library initialization failed: %s" , psc_desc(master_error->status_code)); } } } while(0); | |||
686 | for (int ndx = 0; ndx < master_error->cause_ct; ndx++) { | |||
687 | SYSLOG2(DDCA_SYSLOG_ERROR, "%s", master_error->causes[ndx]->detail)do { if (test_emit_syslog(DDCA_SYSLOG_ERROR)) { int syslog_priority = syslog_importance_from_ddcutil_syslog_level(DDCA_SYSLOG_ERROR ); if (syslog_priority >= 0) { syslog(syslog_priority, "%s" , master_error->causes[ndx]->detail); } } } while(0); | |||
688 | } | |||
689 | } | |||
690 | if (enable_init_msgs) { | |||
691 | printf("(%s) calling report_parse_errors()\n", __func__); | |||
692 | report_parse_errors(master_error); | |||
693 | } | |||
694 | errinfo_free(master_error); | |||
695 | library_initialization_failed = true1; | |||
696 | } | |||
697 | else { | |||
698 | i2c_detect_buses(); | |||
699 | ddc_ensure_displays_detected(); | |||
700 | if (parsed_cmd->flags&CMD_FLAG_WATCH_DISPLAY_HOTPLUG_EVENTS) | |||
| ||||
701 | ddc_start_watch_displays(); | |||
702 | library_initialized = true1; | |||
703 | library_initialization_failed = false0; | |||
704 | SYSLOG2(DDCA_SYSLOG_NOTICE, "Library initialization complete.")do { if (test_emit_syslog(DDCA_SYSLOG_NOTICE)) { int syslog_priority = syslog_importance_from_ddcutil_syslog_level(DDCA_SYSLOG_NOTICE ); if (syslog_priority >= 0) { syslog(syslog_priority, "Library initialization complete." ); } } } while(0); | |||
705 | } | |||
706 | free_parsed_cmd(parsed_cmd); | |||
707 | ||||
708 | DBGF(debug, "Done. Returning: %s", psc_desc(ddcrc))do { if (debug) simple_dbgmsg(debug, __func__, 708, "api_base.c" , "Done. Returning: %s", psc_desc(ddcrc)); } while(0); | |||
709 | ||||
710 | return ddcrc; | |||
711 | } | |||
712 | ||||
713 | ||||
714 | DDCA_Status | |||
715 | ddca_init(const char * libopts, | |||
716 | DDCA_Syslog_Level syslog_level_arg, | |||
717 | DDCA_Init_Options opts) | |||
718 | { | |||
719 | return ddci_init(libopts, syslog_level_arg, opts, NULL((void*)0)); | |||
720 | } | |||
721 | ||||
722 | DDCA_Status | |||
723 | ddca_init2(const char * libopts, | |||
724 | DDCA_Syslog_Level syslog_level_arg, | |||
725 | DDCA_Init_Options opts, | |||
726 | char*** infomsg_loc | |||
727 | ) | |||
728 | { | |||
729 | return ddci_init(libopts, syslog_level_arg, opts, infomsg_loc); | |||
730 | } | |||
731 | ||||
732 | ||||
733 | ||||
734 | DDCA_Status | |||
735 | ddca_start_watch_displays() { | |||
736 | bool_Bool debug = false0; | |||
737 | API_PROLOG(debug, "Starting")do { if (!library_initialized) { ddca_init(((void*)0), DDCA_SYSLOG_NOTICE , DDCA_INIT_OPTIONS_DISABLE_CONFIG_FILE); } if (trace_api_call_depth > 0 || is_traced_api_call(__func__) ) trace_api_call_depth ++; dbgtrc( (debug) ? DDCA_TRC_ALL : DDCA_TRC_API, 0x00, __func__ , 737, "api_base.c", "Starting ""Starting"); if (ptd_api_profiling_enabled ) ptd_profile_function_start(__func__); } while(0); | |||
738 | ddc_start_watch_displays(false0); | |||
739 | API_EPILOG(debug, DDCRC_OK, "")do { dbgtrc_ret_ddcrc( (debug) ? DDCA_TRC_ALL : DDCA_TRC_API, 0x00, __func__, 739, "api_base.c", 0, ""); if (trace_api_call_depth > 0) trace_api_call_depth--; if (ptd_api_profiling_enabled ) ptd_profile_function_end(__func__); return 0; } while(0); | |||
740 | } | |||
741 | ||||
742 | ||||
743 | ||||
744 | DDCA_Status | |||
745 | ddca_stop_watch_displays(bool_Bool wait) { | |||
746 | bool_Bool debug = false0; | |||
747 | API_PROLOG(debug, "Starting")do { if (!library_initialized) { ddca_init(((void*)0), DDCA_SYSLOG_NOTICE , DDCA_INIT_OPTIONS_DISABLE_CONFIG_FILE); } if (trace_api_call_depth > 0 || is_traced_api_call(__func__) ) trace_api_call_depth ++; dbgtrc( (debug) ? DDCA_TRC_ALL : DDCA_TRC_API, 0x00, __func__ , 747, "api_base.c", "Starting ""Starting"); if (ptd_api_profiling_enabled ) ptd_profile_function_start(__func__); } while(0); | |||
| ||||
748 | ddc_stop_watch_displays(wait); | |||
749 | API_EPILOG(debug, DDCRC_OK, "")do { dbgtrc_ret_ddcrc( (debug) ? DDCA_TRC_ALL : DDCA_TRC_API, 0x00, __func__, 749, "api_base.c", 0, ""); if (trace_api_call_depth > 0) trace_api_call_depth--; if (ptd_api_profiling_enabled ) ptd_profile_function_end(__func__); return 0; } while(0); | |||
750 | } | |||
751 | ||||
752 | ||||
753 | // | |||
754 | // Error Detail | |||
755 | // | |||
756 | ||||
757 | DDCA_Error_Detail * | |||
758 | ddca_get_error_detail() { | |||
759 | bool_Bool debug = false0; | |||
760 | DBGMSF(debug, "Starting")do { if (debug) dbgtrc(DDCA_TRC_ALL, 0x00, __func__, 760, "api_base.c" , "Starting"); } while(0); | |||
761 | ||||
762 | DDCA_Error_Detail * result = dup_error_detail(get_thread_error_detail()); | |||
763 | ||||
764 | if (debug) { | |||
765 | DBGMSG("Done. Returning: %p", result)dbgtrc(DDCA_TRC_ALL, 0x00, __func__, 765, "api_base.c", "Done. Returning: %p" , result); | |||
766 | if (result) | |||
767 | report_error_detail(result, 2); | |||
768 | } | |||
769 | return result; | |||
770 | } | |||
771 | ||||
772 | ||||
773 | void | |||
774 | ddca_free_error_detail(DDCA_Error_Detail * ddca_erec) { | |||
775 | free_error_detail(ddca_erec); | |||
776 | } | |||
777 | ||||
778 | ||||
779 | void | |||
780 | ddca_report_error_detail(DDCA_Error_Detail * ddca_erec, int depth) { | |||
781 | report_error_detail(ddca_erec, depth); | |||
782 | } | |||
783 | ||||
784 | ||||
785 | // DDCA_Error_Detail * ddca_dup_error_detail(DDCA_Error_Detail * original) { | |||
786 | // return dup_error_detail(original); | |||
787 | // } | |||
788 | ||||
789 | ||||
790 | // | |||
791 | // Status Code Management | |||
792 | // | |||
793 | ||||
794 | const char * | |||
795 | ddca_rc_name(DDCA_Status status_code) { | |||
796 | char * result = NULL((void*)0); | |||
797 | Status_Code_Info * code_info = find_status_code_info(status_code); | |||
798 | if (code_info) | |||
799 | result = code_info->name; | |||
800 | return result; | |||
801 | } | |||
802 | ||||
803 | ||||
804 | const char * | |||
805 | ddca_rc_desc(DDCA_Status status_code) { | |||
806 | char * result = "unknown status code"; | |||
807 | Status_Code_Info * code_info = find_status_code_info(status_code); | |||
808 | if (code_info) | |||
809 | result = code_info->description; | |||
810 | return result; | |||
811 | } | |||
812 | ||||
813 | ||||
814 | // | |||
815 | // Output redirection | |||
816 | // | |||
817 | ||||
818 | // Redirects output that normally would go to STDOUT | |||
819 | void | |||
820 | ddca_set_fout(FILE * fout) { | |||
821 | // DBGMSG("Starting. fout=%p", fout); | |||
822 | set_fout(fout); | |||
823 | } | |||
824 | ||||
825 | ||||
826 | void | |||
827 | ddca_set_fout_to_default(void) { | |||
828 | set_fout_to_default(); | |||
829 | } | |||
830 | ||||
831 | ||||
832 | // Redirects output that normally would go to STDERR | |||
833 | void | |||
834 | ddca_set_ferr(FILE * ferr) { | |||
835 | set_ferr(ferr); | |||
836 | } | |||
837 | ||||
838 | ||||
839 | void | |||
840 | ddca_set_ferr_to_default(void) { | |||
841 | set_ferr_to_default(); | |||
842 | } | |||
843 | ||||
844 | ||||
845 | // | |||
846 | // Output capture - convenience functions | |||
847 | // | |||
848 | ||||
849 | void | |||
850 | ddca_start_capture(DDCA_Capture_Option_Flags flags) { | |||
851 | start_capture(flags); | |||
852 | } | |||
853 | ||||
854 | ||||
855 | char * | |||
856 | ddca_end_capture(void) { | |||
857 | return end_capture(); | |||
858 | } | |||
859 | ||||
860 | ||||
861 | ||||
862 | // | |||
863 | // Message Control | |||
864 | // | |||
865 | ||||
866 | DDCA_Output_Level | |||
867 | ddca_get_output_level(void) { | |||
868 | return get_output_level(); | |||
869 | } | |||
870 | ||||
871 | ||||
872 | DDCA_Output_Level | |||
873 | ddca_set_output_level(DDCA_Output_Level newval) { | |||
874 | return set_output_level(newval); | |||
875 | } | |||
876 | ||||
877 | ||||
878 | char * | |||
879 | ddca_output_level_name(DDCA_Output_Level val) { | |||
880 | return output_level_name(val); | |||
881 | } | |||
882 | ||||
883 | ||||
884 | // | |||
885 | // Global Settings | |||
886 | // | |||
887 | ||||
888 | #ifdef REMOVED | |||
889 | int | |||
890 | ddca_max_max_tries(void) { | |||
891 | return MAX_MAX_TRIES15; | |||
892 | } | |||
893 | ||||
894 | ||||
895 | // *** THIS IS FOR THE CURRENT THREAD | |||
896 | // *** replace using function specifying display | |||
897 | // *** for now, revert to old try_data_get_maxtries2() | |||
898 | int | |||
899 | ddca_get_max_tries(DDCA_Retry_Type retry_type) { | |||
900 | // stats for multi part writes and reads are separate, but the | |||
901 | // max tries for both are identical | |||
902 | // #ifndef NDEBUG | |||
903 | Retry_Op_Value result3 = try_data_get_maxtries2((Retry_Operation) retry_type); | |||
904 | // #endif | |||
905 | // // new way using retry_mgt | |||
906 | // Retry_Op_Value result2 = trd_get_thread_max_tries((Retry_Operation) retry_type); | |||
907 | // assert(result == result2); | |||
908 | // assert(result2 == result3); | |||
909 | return result3; | |||
910 | } | |||
911 | ||||
912 | ||||
913 | // ** THIS IS FOR CURRENT THREAD - FIX | |||
914 | DDCA_Status | |||
915 | ddca_set_max_tries( | |||
916 | DDCA_Retry_Type retry_type, | |||
917 | int max_tries) | |||
918 | { | |||
919 | DDCA_Status rc = 0; | |||
920 | free_thread_error_detail(); | |||
921 | if (max_tries < 1 || max_tries > MAX_MAX_TRIES15) | |||
922 | rc = DDCRC_ARG(-(3000 +13) ); | |||
923 | else { | |||
924 | try_data_set_maxtries2((Retry_Operation) retry_type, max_tries); | |||
925 | // for DDCA_MULTI_PART_TRIES, set both MULTI_PART_WRITE_OP and MULTI_PART_READ_OP | |||
926 | if (retry_type == DDCA_MULTI_PART_TRIES) | |||
927 | try_data_set_maxtries2(MULTI_PART_WRITE_OP, max_tries); | |||
928 | ||||
929 | // new way, set in retry_mgt | |||
930 | #ifdef TRD | |||
931 | trd_set_thread_max_tries((Retry_Operation) retry_type, max_tries); | |||
932 | if (retry_type == DDCA_MULTI_PART_TRIES) | |||
933 | trd_set_thread_max_tries(MULTI_PART_WRITE_OP, max_tries); | |||
934 | #endif | |||
935 | } | |||
936 | return rc; | |||
937 | } | |||
938 | #endif | |||
939 | ||||
940 | bool_Bool | |||
941 | ddca_enable_verify(bool_Bool onoff) { | |||
942 | return ddc_set_verify_setvcp(onoff); | |||
943 | } | |||
944 | ||||
945 | ||||
946 | bool_Bool | |||
947 | ddca_is_verify_enabled() { | |||
948 | return ddc_get_verify_setvcp(); | |||
949 | } | |||
950 | ||||
951 | ||||
952 | #ifdef REMOVED | |||
953 | ||||
954 | // *** FOR CURRENT THREAD | |||
955 | double | |||
956 | ddca_set_default_sleep_multiplier(double multiplier) | |||
957 | { | |||
958 | bool_Bool debug = false0; | |||
959 | DBGTRC_STARTING(debug, DDCA_TRC_API, "Setting multiplier = %6.3f", multiplier)dbgtrc( (debug || trace_callstack_call_depth > 0 || is_traced_callstack_call (__func__) ) ? DDCA_TRC_ALL : (DDCA_TRC_API), 0x08, __func__, 959, "api_base.c", "Starting ""Setting multiplier = %6.3f", multiplier); | |||
960 | ||||
961 | double old_value = -1.0; | |||
962 | if (multiplier >= 0.0 && multiplier <= 10.0) { | |||
963 | // #ifdef TSD | |||
964 | old_value = pdd_get_default_sleep_multiplier_factor(); | |||
965 | pdd_set_default_sleep_multiplier_factor(multiplier, Reset); | |||
966 | // #endif | |||
967 | } | |||
968 | ||||
969 | DBGTRC_DONE(debug, DDCA_TRC_API, "Returning: %6.3f", old_value)dbgtrc( (debug) || trace_callstack_call_depth > 0 ? DDCA_TRC_ALL : (DDCA_TRC_API), 0x10, __func__, 969, "api_base.c", "Done " "Returning: %6.3f", old_value); | |||
970 | return old_value; | |||
971 | } | |||
972 | ||||
973 | ||||
974 | double | |||
975 | ddca_get_default_sleep_multiplier() | |||
976 | { | |||
977 | bool_Bool debug = false0; | |||
978 | DBGTRC_STARTING(debug, DDCA_TRC_API, "")dbgtrc( (debug || trace_callstack_call_depth > 0 || is_traced_callstack_call (__func__) ) ? DDCA_TRC_ALL : (DDCA_TRC_API), 0x08, __func__, 978, "api_base.c", "Starting """); | |||
979 | double result = pdd_get_default_sleep_multiplier_factor(); | |||
980 | DBGTRC(debug, DDCA_TRC_API, "Returning %6.3f", result)dbgtrc( (debug) ? DDCA_TRC_ALL : (DDCA_TRC_API), 0x00, __func__ , 980, "api_base.c", "Returning %6.3f", result); | |||
981 | return result; | |||
982 | } | |||
983 | ||||
984 | ||||
985 | void | |||
986 | ddca_set_global_sleep_multiplier(double multiplier) | |||
987 | { | |||
988 | ddca_set_default_sleep_multiplier(multiplier); | |||
989 | return; | |||
990 | } | |||
991 | ||||
992 | double | |||
993 | ddca_get_global_sleep_multiplier() | |||
994 | { | |||
995 | return ddca_get_default_sleep_multiplier(); | |||
996 | } | |||
997 | #endif | |||
998 | ||||
999 | ||||
1000 | double | |||
1001 | ddca_set_sleep_multiplier(double multiplier) | |||
1002 | { | |||
1003 | bool_Bool debug = false0; | |||
1004 | DBGTRC_STARTING(debug, DDCA_TRC_API, "Setting multiplier = %6.3f", multiplier)dbgtrc( (debug || trace_callstack_call_depth > 0 || is_traced_callstack_call (__func__) ) ? DDCA_TRC_ALL : (DDCA_TRC_API), 0x08, __func__, 1004, "api_base.c", "Starting ""Setting multiplier = %6.3f" , multiplier); | |||
1005 | ||||
1006 | double old_value = -1.0; | |||
1007 | if (multiplier >= 0.0 && multiplier <= 10.0) { | |||
1008 | Per_Thread_Data * ptd = ptd_get_per_thread_data(); | |||
1009 | old_value = ptd->sleep_multiplier; | |||
1010 | ptd->sleep_multiplier = multiplier; | |||
1011 | } | |||
1012 | ||||
1013 | DBGTRC_DONE(debug, DDCA_TRC_API, "Returning: %6.3f", old_value)dbgtrc( (debug) || trace_callstack_call_depth > 0 ? DDCA_TRC_ALL : (DDCA_TRC_API), 0x10, __func__, 1013, "api_base.c", "Done " "Returning: %6.3f", old_value); | |||
1014 | return old_value; | |||
1015 | } | |||
1016 | ||||
1017 | double | |||
1018 | ddca_get_sleep_multiplier() | |||
1019 | { | |||
1020 | bool_Bool debug = false0; | |||
1021 | DBGTRC(debug, DDCA_TRC_API, "")dbgtrc( (debug) ? DDCA_TRC_ALL : (DDCA_TRC_API), 0x00, __func__ , 1021, "api_base.c", ""); | |||
1022 | ||||
1023 | Per_Thread_Data * ptd = ptd_get_per_thread_data(); | |||
1024 | double result = ptd->sleep_multiplier; | |||
1025 | ||||
1026 | DBGTRC(debug, DDCA_TRC_API, "Returning %6.3f", result)dbgtrc( (debug) ? DDCA_TRC_ALL : (DDCA_TRC_API), 0x00, __func__ , 1026, "api_base.c", "Returning %6.3f", result); | |||
1027 | return result; | |||
1028 | } | |||
1029 | ||||
1030 | ||||
1031 | #ifdef FUTURE | |||
1032 | ||||
1033 | /** Gets the I2C timeout in milliseconds for the specified timeout class. | |||
1034 | * @param timeout_type timeout type | |||
1035 | * @return timeout in milliseconds | |||
1036 | */ | |||
1037 | int | |||
1038 | ddca_get_timeout_millis( | |||
1039 | DDCA_Timeout_Type timeout_type) { | |||
1040 | return 0; // *** UNIMPLEMENTED *** | |||
1041 | } | |||
1042 | ||||
1043 | /** Sets the I2C timeout in milliseconds for the specified timeout class | |||
1044 | * @param timeout_type timeout class | |||
1045 | * @param millisec timeout to set, in milliseconds | |||
1046 | */ | |||
1047 | void | |||
1048 | ddca_set_timeout_millis( | |||
1049 | DDCA_Timeout_Type timeout_type, | |||
1050 | int millisec) | |||
1051 | { | |||
1052 | // *** UNIMPLEMENTED | |||
1053 | } | |||
1054 | #endif | |||
1055 | ||||
1056 | ||||
1057 | #ifdef REMOVED | |||
1058 | ||||
1059 | /** Controls the force I2C slave address setting. | |||
1060 | * | |||
1061 | * Normally, ioctl operation I2C_SLAVE is used to set the I2C slave address. | |||
1062 | * If that returns EBUSY and this setting is in effect, slave address setting | |||
1063 | * is retried using operation I2C_SLAVE_FORCE. | |||
1064 | * | |||
1065 | * @param[in] onoff true/false | |||
1066 | * @return prior value | |||
1067 | * @since 1.2.2 | |||
1068 | */ | |||
1069 | bool_Bool | |||
1070 | ddca_enable_force_slave_address(bool_Bool onoff); | |||
1071 | ||||
1072 | /** Query the force I2C slave address setting. | |||
1073 | * | |||
1074 | * @return true/false | |||
1075 | * @since 1.2.2 | |||
1076 | */ | |||
1077 | bool_Bool | |||
1078 | ddca_is_force_slave_address_enabled(void); | |||
1079 | #endif | |||
1080 | ||||
1081 | #ifdef REMOVED | |||
1082 | bool_Bool | |||
1083 | ddca_enable_force_slave_address(bool_Bool onoff) { | |||
1084 | bool_Bool old = i2c_forceable_slave_addr_flag; | |||
1085 | i2c_forceable_slave_addr_flag = onoff; | |||
1086 | return old; | |||
1087 | } | |||
1088 | ||||
1089 | ||||
1090 | bool_Bool | |||
1091 | ddca_is_force_slave_address_enabled(void) { | |||
1092 | return i2c_forceable_slave_addr_flag; | |||
1093 | } | |||
1094 | #endif | |||
1095 | ||||
1096 | ||||
1097 | // | |||
1098 | // Statistics | |||
1099 | // | |||
1100 | ||||
1101 | // TODO: Add functions to access ddcutil's runtime error statistics | |||
1102 | ||||
1103 | void | |||
1104 | ddca_reset_stats(void) { | |||
1105 | // DBGMSG("Executing"); | |||
1106 | ddc_reset_stats_main(); | |||
1107 | } | |||
1108 | ||||
1109 | // TODO: Functions that return stats in data structures | |||
1110 | void | |||
1111 | ddca_show_stats( | |||
1112 | DDCA_Stats_Type stats_types, | |||
1113 | bool_Bool per_display_stats, | |||
1114 | int depth) | |||
1115 | { | |||
1116 | if (stats_types) | |||
1117 | ddc_report_stats_main( stats_types, per_display_stats, per_display_stats, false0, depth); | |||
1118 | } | |||
1119 | ||||
1120 | void | |||
1121 | ddca_report_locks( | |||
1122 | int depth) | |||
1123 | { | |||
1124 | dbgrpt_display_locks(depth); | |||
1125 | } | |||
1126 | ||||
1127 | ||||
1128 | void init_api_base() { | |||
1129 | // DBGMSG("Executing"); | |||
1130 | RTTI_ADD_FUNC(_ddca_terminate)rtti_func_name_table_add(_ddca_terminate, "_ddca_terminate");; | |||
1131 | RTTI_ADD_FUNC(ddca_start_watch_displays)rtti_func_name_table_add(ddca_start_watch_displays, "ddca_start_watch_displays" );; | |||
1132 | RTTI_ADD_FUNC(ddca_stop_watch_displays)rtti_func_name_table_add(ddca_stop_watch_displays, "ddca_stop_watch_displays" );; | |||
1133 | #ifdef REMOVED | |||
1134 | RTTI_ADD_FUNC(ddca_set_sleep_multiplier)rtti_func_name_table_add(ddca_set_sleep_multiplier, "ddca_set_sleep_multiplier" );; | |||
1135 | RTTI_ADD_FUNC(ddca_set_default_sleep_multiplier)rtti_func_name_table_add(ddca_set_default_sleep_multiplier, "ddca_set_default_sleep_multiplier" );; | |||
1136 | #endif | |||
1137 | } | |||
1138 |