Bug Summary

File:battstat/apmlib/apmlib.c
Warning:line 102, column 6
Read function called when stream is in EOF state. Function has no effect

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name apmlib.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/rootdir/battstat/apmlib -fcoverage-compilation-dir=/rootdir/battstat/apmlib -resource-dir /usr/lib/llvm-19/lib/clang/19 -D HAVE_CONFIG_H -I . -I ../.. -I ../.. -I ../../apmlib -D G_LOG_DOMAIN="battstat_applet" -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /rootdir/html-report/2025-03-27-110501-83963-1 -x c apmlib.c
1/* apmlib.c -- Sample APM interface routines
2 * Created: Mon Jan 8 10:28:16 1996 by faith@acm.org
3 * Revised: Fri Dec 26 21:38:29 1997 by faith@acm.org
4 * Copyright 1996, 1997 Rickard E. Faith (faith@acm.org)
5 *
6 * This library is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Library General Public License as published
8 * by the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 */
21#include <stdio.h>
22#include <stdlib.h>
23#include <unistd.h>
24#include <string.h>
25#include <fcntl.h>
26#include <ctype.h>
27#include <sys/stat.h>
28#include <sys/time.h>
29#include <sys/ioctl.h>
30#include <sys/types.h>
31#include <sys/sysmacros.h>
32#include "apm.h"
33
34#define BACKWARD_COMPAT1 1
35
36/* If APM support of the right version exists in kernel, return zero.
37 * Otherwise, return 1 if no support exists, or 2 if it is the wrong
38 * version. *NOTE* The sense of the return value is not intuitive.
39 */
40int apm_exists(void)
41{
42 apm_info i;
43
44 if (access(APM_PROC"/proc/apm", R_OK4))
45 return 1;
46 return apm_read(&i);
47}
48
49
50/* Read information from /proc/apm. Return 0 on success, 1 if APM not
51 * installed, 2 if APM installed, but old version.
52 */
53int apm_read(apm_info * i)
54{
55 FILE *str;
56 char units[10];
57 char buffer[100];
58 int retcode = 0;
59
60 if (!(str = fopen(APM_PROC"/proc/apm", "r")))
2
Assuming 'str' is non-null
3
Taking false branch
61 return 1;
62
63 if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0))
4
Assuming stream reaches end-of-file here
5
Taking true branch
64 printf("fgets error\n");
65
66 buffer[sizeof(buffer) - 1] = '\0';
67
68 /* Should check for other driver versions; driver 1.9 (and some
69 * others) uses this format, which doesn't expose # batteries.
70 */
71 sscanf(buffer, "%s %d.%d %x %x %x %x %d%% %d %s\n",
72 (char *) i->driver_version,
73 &i->apm_version_major,
74 &i->apm_version_minor,
75 &i->apm_flags,
76 &i->ac_line_status,
77 &i->battery_status,
78 &i->battery_flags,
79 &i->battery_percentage,
80 &i->battery_time,
81 units);
82 i->using_minutes = !strncmp(units, "min", 3) ? 1 : 0;
6
Assuming the condition is false
7
'?' condition is false
83 if (i->driver_version[0] == 'B')
8
Assuming the condition is true
9
Taking true branch
84 { /* old style. argh. */
85#if !BACKWARD_COMPAT1
86 retcode = 2;
87#else
88 strcpy((char *) i->driver_version, "pre-0.7");
89 i->apm_version_major = 0;
90 i->apm_version_minor = 0;
91 i->apm_flags = 0;
92 i->ac_line_status = 0xff;
93 i->battery_status = 0xff;
94 i->battery_flags = 0xff;
95 i->battery_percentage = -1;
96 i->battery_time = -1;
97 i->using_minutes = 1;
98
99 sscanf(buffer, "BIOS version: %d.%d",
100 &i->apm_version_major, &i->apm_version_minor);
101
102 if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0))
10
Read function called when stream is in EOF state. Function has no effect
103 printf("fgets error\n");
104
105 sscanf(buffer, "Flags: 0x%02x", &i->apm_flags);
106 if (i->apm_flags & APM_32_BIT_SUPPORT0x0002)
107 {
108 if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0))
109 printf("fgets error\n");
110
111 if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0))
112 printf("fgets error\n");
113
114 if (buffer[0] != 'P')
115 {
116 if (!strncmp(buffer + 4, "off line", 8))
117 i->ac_line_status = 0;
118 else if (!strncmp(buffer + 4, "on line", 7))
119 i->ac_line_status = 1;
120 else if (!strncmp(buffer + 4, "on back", 7))
121 i->ac_line_status = 2;
122
123 if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0))
124 printf("fgets error\n");
125
126 if (!strncmp(buffer + 16, "high", 4))
127 i->battery_status = 0;
128 else if (!strncmp(buffer + 16, "low", 3))
129 i->battery_status = 1;
130 else if (!strncmp(buffer + 16, "crit", 4))
131 i->battery_status = 2;
132 else if (!strncmp(buffer + 16, "charg", 5))
133 i->battery_status = 3;
134
135 if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0))
136 printf("fgets error\n");
137
138 if (strncmp(buffer + 14, "unknown", 7))
139 i->battery_percentage = atoi(buffer + 14);
140 if (i->apm_version_major >= 1 && i->apm_version_minor >= 1)
141 {
142 if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0))
143 printf("fgets error\n");
144
145 sscanf(buffer, "Battery flag: 0x%02x", &i->battery_flags);
146
147 if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0))
148 printf("fgets error\n");
149
150 if (strncmp(buffer + 14, "unknown", 7))
151 i->battery_time = atoi(buffer + 14);
152 }
153 }
154 }
155#endif
156 }
157
158 /* Fix possible kernel bug -- percentage
159 * set to 0xff (==255) instead of -1.
160 */
161 if (i->battery_percentage > 100)
162 i->battery_percentage = -1;
163
164 fclose(str);
165 return retcode;
166}
167
168
169/* Lookup the device number for the apm_bios device. */
170dev_t apm_dev(void)
171{
172 FILE *str;
173 static int cached = -1;
174 char buf[80];
175 char *pt;
176 apm_info i;
177
178 if (cached >= 0)
179 return cached;
180
181 if (access(APM_PROC"/proc/apm", R_OK4) || apm_read(&i) == 1)
182 return cached = -1;
183 if (i.driver_version[0] == '1')
184 return cached = makedev(10, 134)gnu_dev_makedev (10, 134);
185
186 if (!(str = fopen(APM_DEV"/proc/devices", "r")))
187 return -1;
188 while (fgets(buf, sizeof(buf) - 1, str))
189 {
190 buf[sizeof(buf) - 1] = '\0';
191 for (pt = buf; *pt && isspace(*pt)((*__ctype_b_loc ())[(int) ((*pt))] & (unsigned short int
) _ISspace)
; ++pt); /* skip leading spaces */
192 for (; *pt && !isspace(*pt)((*__ctype_b_loc ())[(int) ((*pt))] & (unsigned short int
) _ISspace)
; ++pt); /* find next space */
193 if (isspace(*pt)((*__ctype_b_loc ())[(int) ((*pt))] & (unsigned short int
) _ISspace)
)
194 {
195 *pt++ = '\0';
196 pt[strlen(pt) - 1] = '\0'; /* get rid of newline */
197 if (!strcmp(pt, APM_NAME"apm_bios"))
198 {
199 fclose(str);
200 return cached = makedev(atoi(buf), 0)gnu_dev_makedev (atoi(buf), 0);
201 }
202 }
203 }
204 fclose(str);
205 return cached = -1;
206}
207
208
209/* Return a file descriptor for the apm_bios device, or -1 if there is an
210 * error. Is this method secure? Should we make the device in /dev
211 * instead of /tmp?
212 *
213 * apenwarr 2001/05/11: just throw out the weird temporary device file stuff.
214 * It was only for ancient kernel versions anyway.
215 */
216int apm_open(void)
217{
218 int fd;
219 apm_info i;
220
221 if (access(APM_PROC"/proc/apm", R_OK4) || apm_read(&i) == 1)
1
Calling 'apm_read'
222 return -1;
223 if (i.driver_version[0] >= '1')
224 {
225 if ((fd = open(APM_DEVICE"/dev/apm_bios", O_RDWR02)) < 0)
226 {
227 /* Try to create it. This is reasonable
228 * for backward compatibility.
229 */
230 if (mknod(APM_DEVICE"/dev/apm_bios", S_IFCHR0020000 | S_IRUSR0400 | S_IWUSR0200, apm_dev()))
231 {
232 unlink(APM_DEVICE"/dev/apm_bios");
233 return -1;
234 }
235 fd = open(APM_DEVICE"/dev/apm_bios", O_RDWR02);
236 }
237
238 return fd;
239 }
240
241 return -1;
242}
243
244
245/* Given a file descriptor for the apm_bios device, close it. */
246int apm_close(int fd)
247{
248 return close(fd);
249}
250
251/* Given a file descriptor for the apm_bios device, this routine will wait
252 * timeout seconds for APM events. Up to n events will be placed in the
253 * events queue. The return code will indicate the number of events
254 * stored. Since this routine uses select(2), it will return if an
255 * unblocked signal is caught. A timeout < 0 means to block indefinately.
256 *
257 * Note that if you read a request to standby or to suspend, the kernel
258 * will be waiting for you to respond to it with a call to apm_suspend()
259 * or to apm_standby() !
260 */
261int apm_get_events(int fd, int timeout, apm_event_t * events, int n)
262{
263 int retcode;
264 fd_set fds;
265 struct timeval t;
266
267 t.tv_sec = timeout;
268 t.tv_usec = 0;
269
270 FD_ZERO(&fds)do { unsigned int __i; fd_set *__arr = (&fds); for (__i =
0; __i < sizeof (fd_set) / sizeof (__fd_mask); ++__i) ((__arr
)->__fds_bits)[__i] = 0; } while (0)
;
271 FD_SET(fd, &fds)((void) (((&fds)->__fds_bits)[((fd) / (8 * (int) sizeof
(__fd_mask)))] |= ((__fd_mask) (1UL << ((fd) % (8 * (int
) sizeof (__fd_mask)))))))
;
272 retcode = select(fd + 1, &fds, NULL((void*)0), NULL((void*)0), timeout < 0 ? NULL((void*)0) : &t);
273 if (retcode <= 0)
274 return 0;
275 return read(fd, events, n * sizeof(apm_event_t)) / sizeof(apm_event_t);
276}
277
278
279/* Try to set the Power State to Suspend. */
280int apm_suspend(int fd)
281{
282 sync();
283 return ioctl(fd, APM_IOC_SUSPEND(((0U) << (((0 +8)+8)+14)) | ((('A')) << (0 +8)) |
(((2)) << 0) | ((0) << ((0 +8)+8)))
, NULL((void*)0));
284}
285
286
287/* Try to set the Power State to Standby. */
288int apm_standby(int fd)
289{
290 sync();
291 return ioctl(fd, APM_IOC_STANDBY(((0U) << (((0 +8)+8)+14)) | ((('A')) << (0 +8)) |
(((1)) << 0) | ((0) << ((0 +8)+8)))
, NULL((void*)0));
292}
293
294/* Return the last error code generated by the kernel APM driver */
295unsigned int apm_last_error( int fd )
296{
297 int err = 0;
298
299#ifdef APM_IOC_LAST_ERROR
300 int ierr = 0;
301 if ( (ierr = ioctl( fd, APM_IOC_LAST_ERROR, &err)) )
302 return ierr;
303#endif
304 return err;
305}
306
307/* Define lookup table for error messages */
308typedef struct lookup_t {
309 int key;
310 char * msg;
311} lookup_t;
312
313/* APM error messages, arranged by error code */
314static const lookup_t error_table[] = {
315/* N/A { APM_SUCCESS, "Operation succeeded" }, */
316 { APM_DISABLED0x01, "Power management disabled" },
317 { APM_CONNECTED0x02, "Real mode interface already connected" },
318 { APM_NOT_CONNECTED0x03, "Interface not connected" },
319 { APM_16_CONNECTED0x05, "16 bit interface already connected" },
320/* N/A { APM_16_UNSUPPORTED, "16 bit interface not supported" }, */
321 { APM_32_CONNECTED0x07, "32 bit interface already connected" },
322 { APM_32_UNSUPPORTED0x08, "32 bit interface not supported" },
323 { APM_BAD_DEVICE0x09, "Unrecognized device ID" },
324 { APM_BAD_PARAM0x0a, "Parameter out of range" },
325 { APM_NOT_ENGAGED0x0b, "Interface not engaged" },
326#ifdef APM_BAD_FUNCTION0x0c
327 { APM_BAD_FUNCTION0x0c, "Function not supported" },
328#endif
329#ifdef APM_RESUME_DISABLED0x0d
330 { APM_RESUME_DISABLED0x0d, "Resume timer disabled" },
331#endif
332 { APM_BAD_STATE0x60, "Unable to enter requested state" },
333/* N/A { APM_NO_EVENTS, "No events pending" }, */
334 { APM_NOT_PRESENT0x86, "No APM present" }
335};
336#define ERROR_COUNT(sizeof(error_table)/sizeof(lookup_t)) (sizeof(error_table)/sizeof(lookup_t))
337
338/* Return character string describing error messages from APM kernel */
339const char *apm_error_name( unsigned int err )
340{
341 int i;
342
343 for(i=0; i<ERROR_COUNT(sizeof(error_table)/sizeof(lookup_t)); i++)
344 if(err == error_table[i].key) return(error_table[i].msg);
345
346 return "Unknown error";
347}
348
349int apm_reject( int fd )
350{
351#ifdef APM_IOC_REJECT
352 if ( ioctl( fd, APM_IOC_REJECT, NULL((void*)0) ) )
353 return apm_last_error( fd );
354 else
355#endif
356 return 0;
357}
358
359#ifdef APM_IOC_IGNORE /* detect kernel support of IGNORE/NOIGNORE functions */
360int apm_set_ignore(int fd, int mode)
361/* Ignore Standby. */
362{
363 if (mode == IGNORE2)
364 {
365 printf("Telling kernel to ignore system standby/suspend mode\n");
366 return ioctl(fd, APM_IOC_IGNORE, NULL((void*)0));
367 }
368 else
369 {
370 printf("Telling kernel not to ignore system standby/suspend mode\n");
371 return ioctl(fd, APM_IOC_NOIGNORE, NULL((void*)0));
372 }
373 printf("NOTE: User-generated suspend/standby requests are not ignored\n");
374}
375#endif
376
377/* Return a string describing the event. From p. 16 of the Intel/Microsoft
378 * Advanded Power Management (APM) BIOS Interface Specification, Revision
379 * 1.1 (September 1993). Intel Order Number: 241704-001. Microsoft Part
380 * Number: 781-110-X01.
381 *
382 * Updated to APM BIOS 1.2 spec (February 1996). Available on-line.
383 */
384const char *apm_event_name(apm_event_t event)
385{
386 switch (event)
387 {
388 case APM_SYS_STANDBY0x0001:
389 return "System Standby Request";
390 case APM_SYS_SUSPEND0x0002:
391 return "System Suspend Request";
392 case APM_NORMAL_RESUME0x0003:
393 return "Normal Resume System";
394 case APM_CRITICAL_RESUME0x0004:
395 return "Critical Resume System";
396 case APM_LOW_BATTERY0x0005:
397 return "Battery Low";
398 case APM_POWER_STATUS_CHANGE0x0006:
399 return "Power Status Change";
400 case APM_UPDATE_TIME0x0007:
401 return "Update Time";
402 case APM_CRITICAL_SUSPEND0x0008:
403 return "Critical Suspend";
404 case APM_USER_STANDBY0x0009:
405 return "User System Standby Request";
406 case APM_USER_SUSPEND0x000a:
407 return "User System Suspend Request";
408 case APM_STANDBY_RESUME0x000b:
409 return "System Standby Resume";
410#ifdef APM_CAPABILITY_CHANGE0x000c
411 case APM_CAPABILITY_CHANGE0x000c:
412 return "Capability Change";
413#endif
414 }
415 return "Unknown";
416}
417
418
419/* This is a convenience function that has nothing to do with APM. It just
420 * formats a time nicely. If you don't like this format, then write your
421 * own.
422 */
423#define SEC_PER_DAY(60*60*24) (60*60*24)
424#define SEC_PER_HOUR(60*60) (60*60)
425#define SEC_PER_MIN(60) (60)
426
427const char *apm_delta_time(time_t then, time_t now)
428{
429 return apm_time(now - then);
430}
431
432const char *apm_time(time_t t)
433{
434 static char buffer[128];
435 unsigned long s, m, h, d;
436
437 d = t / SEC_PER_DAY(60*60*24);
438 t -= d * SEC_PER_DAY(60*60*24);
439 h = t / SEC_PER_HOUR(60*60);
440 t -= h * SEC_PER_HOUR(60*60);
441 m = t / SEC_PER_MIN(60);
442 t -= m * SEC_PER_MIN(60);
443 s = t;
444
445 if (d)
446 sprintf(buffer, "%lu day%s, %02lu:%02lu:%02lu",
447 d, d > 1 ? "s" : "", h, m, s);
448 else
449 sprintf(buffer, "%02lu:%02lu:%02lu", h, m, s);
450
451 if (t == -1)
452 sprintf(buffer, "unknown");
453
454 return buffer;
455}
456
457const char *apm_time_nosec(time_t t)
458{
459 static char buffer[128];
460 unsigned long s, m, h, d;
461
462 d = t / SEC_PER_DAY(60*60*24);
463 t -= d * SEC_PER_DAY(60*60*24);
464 h = t / SEC_PER_HOUR(60*60);
465 t -= h * SEC_PER_HOUR(60*60);
466 m = t / SEC_PER_MIN(60);
467 t -= m * SEC_PER_MIN(60);
468 s = t;
469
470 if (s > 30)
471 ++m;
472
473 if (d)
474 sprintf(buffer, "%lu day%s, %lu:%02lu",
475 d, d > 1 ? "s" : "", h, m);
476 else
477 sprintf(buffer, "%lu:%02lu", h, m);
478
479 if (t == -1)
480 sprintf(buffer, "unknown");
481
482 return buffer;
483}