1+ /**
2+ @file config.c
3+ @brief Configuration Management Module
4+
5+ This module handles INI file parsing, validation, and configuration management
6+ for the Process Watchdog application. It provides functions to load, validate,
7+ and monitor configuration files.
8+
9+ @date 2023-01-01
10+ @version 1.0
11+ @author by Eray Ozturk | [email protected] 12+ @url github.com/diffstorm
13+ @license GPL-3 License
14+ */
15+
16+ #include "config.h"
17+ #define INI_MAX_LINE MAX_APP_CMD_LENGTH
18+ #include "ini.h"
19+ #include "log.h"
20+ #include "utils.h"
21+
22+ #include <stdio.h>
23+ #include <stdlib.h>
24+ #include <stdbool.h>
25+ #include <string.h>
26+ #include <time.h>
27+ #include <sys/stat.h>
28+ #include <errno.h>
29+ #include <limits.h>
30+
31+ //------------------------------------------------------------------
32+ // Private helper functions
33+ //------------------------------------------------------------------
34+
35+ /**
36+ @brief Gets the last modification time of a file.
37+
38+ @param path Path to the file.
39+ @return Last modification time as time_t.
40+ */
41+ static time_t config_get_file_modified_time (const char * path )
42+ {
43+ struct stat attr ;
44+ stat (path , & attr );
45+ return attr .st_mtime ;
46+ }
47+
48+ //------------------------------------------------------------------
49+ // Private parsing functions
50+ //------------------------------------------------------------------
51+
52+ /**
53+ @brief INI file handler function for parsing configuration sections and key-value pairs.
54+
55+ @param user User data (unused).
56+ @param section Section name from INI file.
57+ @param name Key name from INI file.
58+ @param value Value from INI file.
59+ @return 1 on success, 0 on error.
60+ */
61+ static int config_ini_handler (void * user , const char * section , const char * name , const char * value )
62+ {
63+ // Extract apps and state from user data
64+ Application_t * apps = ((void * * )user )[0 ];
65+ AppState_t * state = ((void * * )user )[1 ];
66+
67+ static char last_section [MAX_APP_NAME_LENGTH ] = {0 };
68+ const char * app_prefix = "app:" ;
69+
70+ // New section detected
71+ if (strcmp (section , last_section ) != 0 ) {
72+ if (strncmp (section , app_prefix , strlen (app_prefix )) == 0 ) {
73+ if (state -> app_count < MAX_APPS ) {
74+ const char * app_name = section + strlen (app_prefix );
75+ if (* app_name == '\0' ) {
76+ LOGE ("Empty app name in section header: [%s]" , section );
77+ return 0 ; // Error
78+ }
79+ strncpy (apps [state -> app_count ].name , app_name , MAX_APP_NAME_LENGTH - 1 );
80+ apps [state -> app_count ].name [MAX_APP_NAME_LENGTH - 1 ] = '\0' ;
81+ state -> app_count ++ ;
82+ } else {
83+ LOGW ("MAX_APPS (%d) reached. Ignoring section [%s]" , MAX_APPS , section );
84+ }
85+ }
86+ strncpy (last_section , section , sizeof (last_section ) - 1 );
87+ last_section [sizeof (last_section ) - 1 ] = '\0' ;
88+ }
89+
90+ // Find current app index
91+ int index = -1 ;
92+ if (strncmp (section , app_prefix , strlen (app_prefix )) == 0 ) {
93+ const char * app_name = section + strlen (app_prefix );
94+ for (int i = 0 ; i < state -> app_count ; i ++ ) {
95+ if (strcmp (apps [i ].name , app_name ) == 0 ) {
96+ index = i ;
97+ break ;
98+ }
99+ }
100+ }
101+
102+ // Process key-value pairs
103+ if (strcmp (section , "processWatchdog" ) == 0 ) {
104+ if (strcmp (name , "udp_port" ) == 0 ) {
105+ if (!parse_int (value , 1 , 65535 , & state -> udp_port )) {
106+ LOGE ("Invalid UDP port: %s" , value );
107+ return 0 ;
108+ }
109+ }
110+ } else if (index != -1 ) { // This is an app section we are tracking
111+ if (strcmp (name , "start_delay" ) == 0 ) {
112+ if (!parse_int (value , 0 , INT_MAX , & apps [index ].start_delay )) {
113+ LOGE ("Invalid start_delay for app %s: %s" , apps [index ].name , value );
114+ return 0 ;
115+ }
116+ } else if (strcmp (name , "heartbeat_delay" ) == 0 ) {
117+ if (!parse_int (value , 0 , INT_MAX , & apps [index ].heartbeat_delay )) {
118+ LOGE ("Invalid heartbeat_delay for app %s: %s" , apps [index ].name , value );
119+ return 0 ;
120+ }
121+ } else if (strcmp (name , "heartbeat_interval" ) == 0 ) {
122+ if (!parse_int (value , 0 , INT_MAX , & apps [index ].heartbeat_interval )) {
123+ LOGE ("Invalid heartbeat_interval for app %s: %s" , apps [index ].name , value );
124+ return 0 ;
125+ }
126+ } else if (strcmp (name , "cmd" ) == 0 ) {
127+ snprintf (apps [index ].cmd , MAX_APP_CMD_LENGTH , "%s" , value );
128+ if (strlen (value ) >= MAX_APP_CMD_LENGTH ) {
129+ LOGE ("Invalid cmd for app %s - longer than %d charachters" , apps [index ].name , MAX_APP_CMD_LENGTH );
130+ return 0 ;
131+ }
132+ }
133+ }
134+
135+ return 1 ;
136+ }
137+
138+ //------------------------------------------------------------------
139+ // Public interface functions
140+ //------------------------------------------------------------------
141+
142+ int config_validate_file (const char * ini_path )
143+ {
144+ if (!ini_path || strlen (ini_path ) == 0 || strlen (ini_path ) >= MAX_APP_CMD_LENGTH )
145+ {
146+ return 1 ;
147+ }
148+
149+ if (!f_exist (ini_path ))
150+ {
151+ return 1 ;
152+ }
153+
154+ return 0 ;
155+ }
156+
157+ int config_set_file_path (char * path )
158+ {
159+ if (config_validate_file (path ))
160+ {
161+ LOGE ("Invalid path" );
162+ return 1 ;
163+ }
164+
165+ // Note: This function originally set a global variable app_state.ini_file
166+ // In the new design, the caller should manage the file path
167+ // For now, we just validate and return success
168+ LOGD ("INI file path validated: %s" , path );
169+ return 0 ;
170+ }
171+
172+ bool config_is_file_updated (const char * ini_path , time_t last_modified )
173+ {
174+ time_t file_last_modified_time = config_get_file_modified_time (ini_path );
175+ return (file_last_modified_time != last_modified );
176+ }
177+
178+ int config_parse_file (const char * ini_path , Application_t * apps , int max_apps , AppState_t * state )
179+ {
180+ // Initialize the application array and state
181+ memset (apps , 0 , sizeof (Application_t ) * max_apps );
182+ state -> app_count = 0 ;
183+ state -> uptime = get_uptime ();
184+ state -> udp_port = UDP_PORT ;
185+
186+ LOGD ("Reading ini file %s" , ini_path );
187+
188+ // Prepare user data for the handler
189+ void * user_data [2 ] = { apps , state };
190+
191+ if (ini_parse (ini_path , config_ini_handler , user_data ) < 0 )
192+ {
193+ LOGE ("Failed to parse INI file %s" , ini_path );
194+ return 1 ;
195+ }
196+
197+ LOGD ("%d processes have found in the ini file %s" , state -> app_count , ini_path );
198+ state -> ini_last_modified_time = config_get_file_modified_time (ini_path );
199+ return 0 ;
200+ }
0 commit comments