1
+ /*
2
+ * Copyright (c) 2025, Andy Lemin (andylemin)
3
+ * Credits; Based on work by Netatalk contributors
4
+ *
5
+ * This program is free software; you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation; either version 2 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ */
15
+
16
+ /* Configuration header (must be first) */
17
+ #ifdef HAVE_CONFIG_H
18
+ #include "config.h"
19
+ #endif /* HAVE_CONFIG_H */
20
+
1
21
/* Standard C library includes */
22
+ #include <stdio.h>
23
+
24
+ #ifdef __linux__
25
+
26
+ /* Standard C library includes for Linux implementation */
2
27
#include <errno.h>
3
28
#include <stdbool.h>
4
29
#include <stdint.h>
5
- #include <stdio.h>
6
30
#include <stdlib.h>
7
31
#include <string.h>
8
32
#include <sys/types.h>
9
33
#include <unistd.h>
10
34
35
+ /* Netatalk includes for INIPARSER_GETSTRDUP macro */
36
+ #include <atalk/globals.h>
37
+
38
+ /* Iniparser includes */
39
+ #ifdef HAVE_INIPARSER_INIPARSER_H
40
+ #include <iniparser/iniparser.h>
41
+ #else
42
+ #include <iniparser.h>
43
+ #endif
44
+
11
45
/* External globals from lantest.c */
12
46
extern bool Debug ;
13
47
14
48
/* Constants */
15
49
#define LOG_BUFFER_SIZE (100 * 1024) /* 100KB buffer for reading log file */
16
50
#define MAX_LOG_LINES_DISPLAY 10 /* Number of log lines to show when no stats found */
17
- #define CONFIG_LINE_SIZE 1024 /* Max line size for config file parsing */
18
51
19
52
/* Debug logging macro */
20
53
#define DEBUG_LOG (fmt , ...) \
@@ -23,112 +56,52 @@ extern bool Debug;
23
56
fprintf(stderr, "DEBUG: " fmt "\n", ##__VA_ARGS__); \
24
57
} while(0)
25
58
26
- #ifdef __linux__
27
-
28
59
/* Parse netatalk config file to extract log file path */
29
60
static char * parse_config_for_log_path (void )
30
61
{
31
- FILE * config_fp = NULL ;
62
+ dictionary * iniconfig = NULL ;
32
63
char * log_file_path = NULL ;
33
- const char * config_paths [] = {
34
- "/etc/afp.conf" ,
35
- "/etc/netatalk/afp.conf" ,
36
- "/usr/local/etc/afp.conf" ,
37
- "/usr/local/etc/netatalk/afp.conf" ,
38
- NULL
39
- };
40
-
41
- for (int i = 0 ; config_paths [i ] != NULL ; i ++ ) {
42
- DEBUG_LOG ("Trying to open config file: %s" , config_paths [i ]);
43
-
44
- if (access (config_paths [i ], F_OK ) != 0 ) {
45
- DEBUG_LOG ("Config file does not exist: %s" , config_paths [i ]);
46
- continue ;
47
- }
64
+ DEBUG_LOG ("Loading config file: %safp.conf" , _PATH_CONFDIR );
65
+ /* Load the config file using iniparser */
66
+ iniconfig = iniparser_load (_PATH_CONFDIR "afp.conf" );
48
67
49
- if (access (config_paths [i ], R_OK ) != 0 ) {
50
- DEBUG_LOG ("Config file exists but not readable: %s" , config_paths [i ]);
51
- continue ;
52
- }
68
+ if (!iniconfig ) {
69
+ fprintf (stderr , "INFO: Could not load config file: %safp.conf\n" ,
70
+ _PATH_CONFDIR );
71
+ return NULL ;
72
+ }
53
73
54
- config_fp = fopen (config_paths [i ], "r" );
55
-
56
- if (config_fp ) {
57
- DEBUG_LOG ("Successfully opened config file: %s" , config_paths [i ]);
58
- char line [CONFIG_LINE_SIZE ];
59
-
60
- while (fgets (line , sizeof (line ), config_fp )) {
61
- /* Look for "log file = " directive */
62
- char * ptr = strstr (line , "log file" );
63
-
64
- if (ptr ) {
65
- ptr = strchr (ptr , '=' );
66
-
67
- if (ptr ) {
68
- ptr ++ ;
69
-
70
- /* Extract path */
71
- while (* ptr && (* ptr == ' ' || * ptr == '\t' )) {
72
- ptr ++ ;
73
- }
74
-
75
- char * end = ptr ;
76
-
77
- while (* end && * end != '\n' && * end != '\r' && * end != '#' ) {
78
- end ++ ;
79
- }
80
-
81
- if (end > ptr ) {
82
- * end = '\0' ;
83
- end -- ;
84
-
85
- while (end > ptr && (* end == ' ' || * end == '\t' )) {
86
- * end = '\0' ;
87
- end -- ;
88
- }
89
-
90
- if (ptr && * ptr != '\0' ) {
91
- DEBUG_LOG ("Found log file path: %s" , ptr );
92
- log_file_path = strdup (ptr );
93
-
94
- if (!log_file_path ) {
95
- fprintf (stderr , "ERROR: strdup() failed for log_file_path\n" );
96
- fclose (config_fp );
97
- return NULL ;
98
- }
99
-
100
- break ;
101
- } else {
102
- fprintf (stderr , "WARNING: Log file path was empty after trimming\n" );
103
- }
104
- } else {
105
- fprintf (stderr , "WARNING: No content after '=' in log file line\n" );
106
- }
107
- } else {
108
- fprintf (stderr , "WARNING: No '=' found in log file line\n" );
109
- }
110
- }
111
- }
74
+ /* Get the log file value from the Global section */
75
+ log_file_path = INIPARSER_GETSTRDUP (iniconfig , INISEC_GLOBAL , "log file" , NULL );
112
76
113
- fclose (config_fp );
77
+ if (log_file_path && * log_file_path != '\0' ) {
78
+ DEBUG_LOG ("Found log file path: %s" , log_file_path );
79
+ } else {
80
+ DEBUG_LOG ("No 'log file' configuration found in Global section" );
114
81
115
- if (log_file_path ) {
116
- break ;
117
- }
118
- } else {
119
- fprintf (stderr , "ERROR: Failed to open config file: %s (errno=%d: %s)\n" ,
120
- config_paths [i ], errno , strerror (errno ));
82
+ if (log_file_path ) {
83
+ free (log_file_path );
84
+ log_file_path = NULL ;
121
85
}
122
86
}
123
87
88
+ /* Free the iniparser dictionary */
89
+ iniparser_freedict (iniconfig );
124
90
return log_file_path ;
125
91
}
126
92
127
- /* Read the last portion of log file into buffer */
128
- static ssize_t read_log_tail ( const char * log_file_path , char * buffer ,
129
- size_t buffer_size )
93
+ /* Read the last portion of log file into buffer
94
+ * Buffer must be at least LOG_BUFFER_SIZE + 1 bytes */
95
+ static ssize_t read_log_tail ( const char * log_file_path , char * buffer )
130
96
{
131
97
FILE * log_fp = NULL ;
98
+
99
+ /* Validate input parameters */
100
+ if (!buffer ) {
101
+ fprintf (stderr , "ERROR: Buffer is NULL\n" );
102
+ return -1 ;
103
+ }
104
+
132
105
DEBUG_LOG ("Opening log file: %s" , log_file_path );
133
106
log_fp = fopen (log_file_path , "rb" );
134
107
@@ -164,10 +137,11 @@ static ssize_t read_log_tail(const char *log_file_path, char *buffer,
164
137
return 0 ;
165
138
}
166
139
167
- /* Read from the end, up to buffer_size */
168
- long read_start = (file_size > (long )buffer_size ) ? (file_size -
169
- (long )buffer_size ) : 0 ;
170
- size_t read_size = (file_size > (long )buffer_size ) ? buffer_size :
140
+ /* Read from the end, up to LOG_BUFFER_SIZE to reserve space for null terminator */
141
+ size_t max_read = LOG_BUFFER_SIZE ;
142
+ long read_start = (file_size > (long )max_read ) ? (file_size -
143
+ (long )max_read ) : 0 ;
144
+ size_t read_size = (file_size > (long )max_read ) ? max_read :
171
145
(size_t )file_size ;
172
146
DEBUG_LOG ("Reading %zu bytes from position: %ld" , read_size , read_start );
173
147
@@ -186,6 +160,16 @@ static ssize_t read_log_tail(const char *log_file_path, char *buffer,
186
160
return -1 ;
187
161
}
188
162
163
+ /* Simple validation - fread cannot return more than requested */
164
+ if (bytes_read > read_size ) {
165
+ fprintf (stderr , "ERROR: fread returned more bytes than requested\n" );
166
+ fclose (log_fp );
167
+ return -1 ;
168
+ }
169
+
170
+ /* Null-terminate the buffer
171
+ * Since read_size <= LOG_BUFFER_SIZE and bytes_read <= read_size,
172
+ * we know bytes_read <= LOG_BUFFER_SIZE, so this is always safe */
189
173
buffer [bytes_read ] = '\0' ;
190
174
fclose (log_fp );
191
175
return (ssize_t )bytes_read ;
@@ -195,6 +179,12 @@ static ssize_t read_log_tail(const char *log_file_path, char *buffer,
195
179
static char * find_dircache_stats_line (const char * buffer , size_t bytes_read )
196
180
{
197
181
char * stats_line = NULL ;
182
+
183
+ /* Validate bytes_read before using for allocation */
184
+ if (bytes_read == 0 || bytes_read > LOG_BUFFER_SIZE ) {
185
+ return NULL ;
186
+ }
187
+
198
188
/* Make a working copy of the buffer to avoid const issues */
199
189
char * work_buffer = malloc (bytes_read + 1 );
200
190
@@ -255,6 +245,13 @@ static void display_last_log_lines(const char *buffer, size_t bytes_read)
255
245
printf ("(At least 'log level = default:info' is required)\n" );
256
246
printf ("Last 10 lines of log file:\n" );
257
247
printf ("---------------------------\n" );
248
+
249
+ /* Validate bytes_read before using for allocation */
250
+ if (bytes_read == 0 || bytes_read > LOG_BUFFER_SIZE ) {
251
+ printf ("Invalid buffer size\n" );
252
+ return ;
253
+ }
254
+
258
255
/* Make a working copy of the buffer to avoid const issues */
259
256
char * work_buffer = malloc (bytes_read + 1 );
260
257
@@ -324,12 +321,9 @@ static void display_last_log_lines(const char *buffer, size_t bytes_read)
324
321
}
325
322
}
326
323
327
- #endif /* __linux__ */
328
-
329
324
/* Read and display dircache statistics from log file */
330
- static void display_dircache_statistics (void )
325
+ void display_dircache_statistics (void )
331
326
{
332
- #ifdef __linux__
333
327
char * log_file_path = NULL ;
334
328
char * stats_line = NULL ;
335
329
static char buffer [LOG_BUFFER_SIZE + 1 ];
@@ -343,13 +337,20 @@ static void display_dircache_statistics(void)
343
337
}
344
338
345
339
DEBUG_LOG ("Log file path: %s" , log_file_path );
346
- bytes_read = read_log_tail (log_file_path , buffer , LOG_BUFFER_SIZE );
340
+ bytes_read = read_log_tail (log_file_path , buffer , sizeof ( buffer ) );
347
341
348
342
if (bytes_read <= 0 ) {
349
343
free (log_file_path );
350
344
return ;
351
345
}
352
346
347
+ /* Validate bytes_read is positive and reasonable before casting */
348
+ if (bytes_read < 0 || bytes_read > LOG_BUFFER_SIZE ) {
349
+ fprintf (stderr , "ERROR: Invalid bytes_read value: %zd\n" , bytes_read );
350
+ free (log_file_path );
351
+ return ;
352
+ }
353
+
353
354
stats_line = find_dircache_stats_line (buffer , (size_t )bytes_read );
354
355
printf ("\nDircache Statistics (%s):\n" , log_file_path );
355
356
printf ("------------------------------------------------------------------\n" );
@@ -369,7 +370,14 @@ static void display_dircache_statistics(void)
369
370
}
370
371
371
372
free (log_file_path );
372
- #else
373
- /* Not on Linux, function not available */
374
- #endif /* __linux__ */
375
- }
373
+ }
374
+
375
+ #else /* !__linux__ */
376
+
377
+ /* Stub implementation for non-Linux systems */
378
+ void display_dircache_statistics (void )
379
+ {
380
+ /* Not available on this platform */
381
+ }
382
+
383
+ #endif /* __linux__ */
0 commit comments