-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpgfindlib.c
More file actions
1590 lines (1507 loc) · 73.1 KB
/
pgfindlib.c
File metadata and controls
1590 lines (1507 loc) · 73.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
pgfindlib.c -- To get a list of paths and .so files along paths dynamic loader might choose, with some extra information
Version: 0.9.8
Last modified: September 25 2025
Copyright (c) 2025 by Peter Gulutzan. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
Package: https://github.com/pgulutzan/pgfindlib
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libgen.h>
#include <dirent.h>
#include "pgfindlib.h" /* definition of pgfindlib(), and some #define PGFINDLIB_... */
#if (PGFINDLIB_TOKEN_SOURCE_DT_RPATH_OR_DT_RUNPATH != 0)
#include <elf.h>
#include <link.h>
#endif
/* todo: don't ask for this if we do not stat */
#include <sys/stat.h>
#ifdef PGFINDLIB_FREEBSD
#include <sys/auxv.h>
#endif
#include <errno.h>
struct tokener
{
const char *tokener_name;
short int tokener_length;
char tokener_comment_id;
};
#define PGFINDLIB_REASON_SO_CHECK 1
#define PGFINDLIB_REASON_SO_LIST 2
static int pgfindlib_strcat(char *buffer, unsigned int *buffer_length, const char *line, unsigned int buffer_max_length);
/* todo: make this obsolete */
static int pgfindlib_comment_in_row(char *comment, unsigned int comment_number, int additional_number);
static int pgfindlib_keycmp(const char *a, unsigned int a_len, const char *b);
static int pgfindlib_tokenize(const char *statement, struct tokener tokener_list[],
unsigned int *row_number, ino_t inode_list[], unsigned int *inode_count,
char *buffer, unsigned int *buffer_length, unsigned int buffer_max_length);
static int pgfindlib_file(char *buffer, unsigned int *buffer_length, const char *line, unsigned int buffer_max_length,
unsigned int *row_number,
ino_t inode_list[], unsigned int *inode_count, unsigned int *inode_warning_count,
struct tokener tokener_list_item, int program_e_machine);
static int pgfindlib_find_line_in_statement(const struct tokener tokener_list[], const char *line);
static int pgfindlib_row_version(char *buffer, unsigned int *buffer_length, unsigned buffer_max_length, unsigned int *row_number,
ino_t inode_list[], unsigned int *inode_count);
static int pgfindlib_row_lib(char *buffer, unsigned int *buffer_length, unsigned buffer_max_length, unsigned int *row_number,
ino_t inode_list[], unsigned int *inode_count,
const char *lib, const char *platform, const char *origin);
#if (PGFINDLIB_INCLUDE_ROW_SOURCE_NAME == 1)
static int pgfindlib_row_source_name(char *buffer, unsigned int *buffer_length, unsigned buffer_max_length, unsigned int *row_number,
ino_t inode_list[], unsigned int *inode_count,
const char *source_name);
#endif
static int pgfindlib_replace_lib_or_platform_or_origin(char *one_library_or_file, unsigned int *replacements_count, const char *lib, const char *platform, const char *origin);
static int pgfindlib_get_origin_and_lib_and_platform(char *origin, char *lib, char *platform,
char *buffer, unsigned int *buffer_length, unsigned buffer_max_length,
int *program_e_machine, unsigned int *row_number,
ino_t inode_list[], unsigned int *inode_count);
static int pgfindlib_so_cache(const struct tokener tokener_list[], int tokener_number,
char *malloc_buffer_1, unsigned int *malloc_buffer_1_length, unsigned malloc_buffer_1_max_length,
char **malloc_buffer_2, unsigned int *malloc_buffer_2_length, unsigned malloc_buffer_2_max_length);
static int pgfindlib_add_to_malloc_buffers(const char *new_item, int source_number,
char *malloc_buffer_1, unsigned int *malloc_buffer_1_length, unsigned malloc_buffer_1_max_length,
char **malloc_buffer_2, unsigned int *malloc_buffer_2_length, unsigned malloc_buffer_2_max_length);
static int pgfindlib_row_bottom_level(char *buffer, unsigned int *buffer_length, unsigned int buffer_max_length, unsigned int *row_number,
const char *columns_list[]);
#if (PGFINDLIB_TOKEN_SOURCE_DT_RPATH_OR_DT_RUNPATH != 0)
/* Ordinarily link.h has extern ElfW(Dyn) _DYNAMIC but it's missing with FreeBSD */
extern __attribute__((weak)) ElfW(Dyn) _DYNAMIC[];
#endif
/* pgfindlib_standard_source_array and pgfindlib_standard_source_array_n must match. */
const char *pgfindlib_standard_source_array[] = {"LD_AUDIT", "LD_PRELOAD", "DT_RPATH", "LD_LIBRARY_PATH", "DT_RUNPATH",
"LD_RUN_PATH", "ld.so.cache", "default_paths", "LD_PGFINDLIB_PATH", ""};
const char pgfindlib_standard_source_array_n[]= {PGFINDLIB_TOKEN_SOURCE_LD_AUDIT,PGFINDLIB_TOKEN_SOURCE_LD_PRELOAD,
PGFINDLIB_TOKEN_SOURCE_DT_RPATH, PGFINDLIB_TOKEN_SOURCE_LD_LIBRARY_PATH,
PGFINDLIB_TOKEN_SOURCE_DT_RUNPATH, PGFINDLIB_TOKEN_SOURCE_LD_RUN_PATH,
PGFINDLIB_TOKEN_SOURCE_LD_SO_CACHE, PGFINDLIB_TOKEN_SOURCE_DEFAULT_PATHS,
PGFINDLIB_TOKEN_SOURCE_LD_PGFINDLIB_PATH, 0};
static int pgfindlib_qsort_compare(const void *p1, const void *p2);
static int pgfindlib_source_scan(const char *librarylist, char *buffer, unsigned int *buffer_length,
unsigned int tokener_number, unsigned int buffer_max_length,
const char *lib, const char *platform, const char *origin,
unsigned int *row_number,
ino_t inode_list[], unsigned int *inode_count, unsigned int *inode_warning_count,
struct tokener tokener_list[],
char *malloc_buffer_1, unsigned int *malloc_buffer_1_length, unsigned malloc_buffer_1_max_length,
char **malloc_buffer_2, unsigned int *malloc_buffer_2_length, unsigned malloc_buffer_2_max_length,
int program_e_machine);
static int pgfindlib_read_elf(const struct tokener tokener_list[], const char* possible_elf_file, int reason, int program_e_machine);
static int pgfindlib_comment_is_row(const char *comment, unsigned int comment_number,
char *buffer, unsigned int *buffer_length, unsigned int buffer_max_length, unsigned int *row_number,
ino_t inode_list[], unsigned int *inode_count);
#define PGFINDLIB_FREE_AND_RETURN \
{ \
if (malloc_buffer_1 != NULL) { free(malloc_buffer_1); } \
if (malloc_buffer_2 != NULL) { free(malloc_buffer_2); } \
return rval; \
}
int pgfindlib(const char *statement, char *buffer, unsigned int buffer_max_length)
{
if (buffer == NULL) return PGFINDLIB_ERROR_BUFFER_NULL;
unsigned int buffer_length= 0;
int rval;
unsigned int row_number= 1;
ino_t inode_list[PGFINDLIB_MAX_INODE_COUNT]; /* todo: disable if duplicate checking is off */
unsigned int inode_count= 0;
unsigned int inode_warning_count= 0;
char *malloc_buffer_1= NULL;
char **malloc_buffer_2= NULL;
#if (PGFINDLIB_INCLUDE_ROW_VERSION != 0)
rval= pgfindlib_row_version(buffer, &buffer_length, buffer_max_length, &row_number, inode_list, &inode_count); /* first row including version number */
if (rval != PGFINDLIB_OK) PGFINDLIB_FREE_AND_RETURN;
#endif
int program_e_machine;
char lib[PGFINDLIB_MAX_PATH_LENGTH]= ""; /* Usually this will be changed to whatever $LIB is. */
char platform[PGFINDLIB_MAX_PATH_LENGTH]= ""; /* Usually this will be changed to whatever $LIB is. */
char origin[PGFINDLIB_MAX_PATH_LENGTH]= ""; /* Usually this will be changed to whatever $LIB is. */
rval= pgfindlib_get_origin_and_lib_and_platform(origin, lib, platform, buffer, &buffer_length, buffer_max_length,
&program_e_machine, &row_number, inode_list, &inode_count);
if (rval != PGFINDLIB_OK) PGFINDLIB_FREE_AND_RETURN;
#if (PGFINDLIB_INCLUDE_ROW_LIB != 0)
{
rval= pgfindlib_row_lib(buffer, &buffer_length, buffer_max_length, &row_number, inode_list, &inode_count, lib, platform, origin);
if (rval != PGFINDLIB_OK) PGFINDLIB_FREE_AND_RETURN
}
#endif
/* Put together the list of sources and sonames from the FROM and WHERE of the input. */
/* MAX_TOKENS_COUNT is fixed but more than twice the number of official tokeners */
struct tokener tokener_list[PGFINDLIB_MAX_TOKENS_COUNT];
{
rval= pgfindlib_tokenize(statement, tokener_list, &row_number, inode_list, &inode_count, buffer, &buffer_length, buffer_max_length);
if (rval != PGFINDLIB_OK) PGFINDLIB_FREE_AND_RETURN
}
unsigned int rpath_or_runpath_count= 0;
for (int i= 0; tokener_list[i].tokener_comment_id != PGFINDLIB_TOKEN_END; ++i)
{
if ((tokener_list[i].tokener_comment_id == PGFINDLIB_TOKEN_SOURCE_DT_RPATH)
|| (tokener_list[i].tokener_comment_id == PGFINDLIB_TOKEN_SOURCE_DT_RUNPATH))
++rpath_or_runpath_count;
}
pgfindlib_read_elf(tokener_list, "/tmp/pgfindlib_tests/test", 2, 1);
/* pgfindlib_read_elf(tokener_list, "/home/pgulutzan/pgfindlib/main", 1, 1); */
/* pgfindlib_read_elf(tokener_list, "/lib32/libnsl.so.1", 1, 1); */
/* Preparation if DT_RPATH or DT_RUNPATH */
#if (PGFINDLIB_TOKEN_SOURCE_DT_RPATH_OR_DT_RUNPATH != 0)
const ElfW(Dyn) *dynamic= _DYNAMIC;
const ElfW(Dyn) *dt_rpath= NULL;
const ElfW(Dyn) *dt_runpath= NULL;
const char *dt_strtab= NULL;
if (rpath_or_runpath_count > 0)
{
/* in theory "extern __attribute__((weak)) ... _DYNAMIC[];" could result in _DYNAMIC == NULL */
if (_DYNAMIC == NULL)
{
#if (PGFINDLIB_COMMENT_CANNOT_READ_RPATH != 0)
rval= pgfindlib_comment_is_row("Cannot read DT_RPATH because _DYNAMIC is NULL",
PGFINDLIB_COMMENT_CANNOT_READ_RPATH,
buffer, &buffer_length, buffer_max_length, &row_number, inode_list, &inode_count);
if (rval != PGFINDLIB_OK) PGFINDLIB_FREE_AND_RETURN
#endif
}
else /* _DYNAMIC != NULL */
{
while (dynamic->d_tag != DT_NULL)
{
if (dynamic->d_tag == DT_RPATH) dt_rpath= dynamic;
if (dynamic->d_tag == DT_RUNPATH) dt_runpath= dynamic;
if (dynamic->d_tag == DT_STRTAB) dt_strtab= (const char *)dynamic->d_un.d_val;
++dynamic;
}
}
}
/* So now we have dt_rpath and dt_runpath */
#endif
/* Go through the list of sources and add to the lists: source# length pointer-to-path */
unsigned int malloc_buffer_1_length; unsigned int malloc_buffer_1_max_length;
unsigned int malloc_buffer_2_length; unsigned int malloc_buffer_2_max_length;
malloc_buffer_1_max_length= 1000;
malloc_buffer_2_max_length= 100;
repeat_malloc:
malloc_buffer_1_length= 0;
malloc_buffer_1= (char *)malloc(malloc_buffer_1_max_length);
if (malloc_buffer_1 == NULL) { rval= PGFINDLIB_MALLOC_BUFFER_1_OVERFLOW; PGFINDLIB_FREE_AND_RETURN }
malloc_buffer_2_length= 0;
malloc_buffer_2= (char **)malloc(malloc_buffer_2_max_length * sizeof(char *));
if (malloc_buffer_2 == NULL) { rval= PGFINDLIB_MALLOC_BUFFER_2_OVERFLOW; PGFINDLIB_FREE_AND_RETURN }
for (unsigned int tokener_number= 0; ; ++tokener_number) /* for each source in source name list */
{
int comment_number;
comment_number= tokener_list[tokener_number].tokener_comment_id;
if (comment_number == PGFINDLIB_TOKEN_END) break;
if ((comment_number < PGFINDLIB_TOKEN_SOURCE_LD_AUDIT) || (comment_number > PGFINDLIB_TOKEN_SOURCE_NONSTANDARD)) continue;
char tmp_source_name[PGFINDLIB_MAX_TOKEN_LENGTH + 1]= ""; /* only needed momentarily for nonstandard sources */
const char *ld= NULL;
if (comment_number == PGFINDLIB_TOKEN_SOURCE_DT_RPATH)
{
if ((dt_strtab == NULL) || (dt_rpath == NULL)) continue;
ld= dt_strtab + dt_rpath->d_un.d_val;
}
else if (comment_number == PGFINDLIB_TOKEN_SOURCE_DT_RUNPATH)
{
if ((dt_strtab == NULL) || (dt_runpath == NULL)) continue;
ld= dt_strtab + dt_runpath->d_un.d_val;
}
else if (comment_number == PGFINDLIB_TOKEN_SOURCE_DEFAULT_PATHS)
{
ld= "/lib:/lib64:/usr/lib:/usr/lib64"; /* "lib64" is platform-dependent but dunno which platform */
}
else if (comment_number == PGFINDLIB_TOKEN_SOURCE_NONSTANDARD)
{
int tmp_source_name_length= tokener_list[tokener_number].tokener_length;
memcpy(tmp_source_name, tokener_list[tokener_number].tokener_name, tmp_source_name_length);
tmp_source_name[tmp_source_name_length]= '\0';
ld= tmp_source_name;
}
else if (comment_number != PGFINDLIB_TOKEN_SOURCE_LD_SO_CACHE)
{
/* Not DT_RPATH | DT_RUNPATH | default_paths | nonstandard | ld_so_cache */
/* So it must be LD_AUDIT | LD_PRELOAD | LD_LIBRARY_PATH | LD_RUN_PATH LD_PGFINDLIB_PATH */
int tmp_source_name_length= tokener_list[tokener_number].tokener_length;
memcpy(tmp_source_name, tokener_list[tokener_number].tokener_name, tmp_source_name_length);
tmp_source_name[tmp_source_name_length]= '\0';
ld= getenv(tmp_source_name);
}
if (comment_number == PGFINDLIB_TOKEN_SOURCE_LD_SO_CACHE)
{
rval= pgfindlib_so_cache(tokener_list, tokener_number,
malloc_buffer_1, &malloc_buffer_1_length, malloc_buffer_1_max_length,
malloc_buffer_2, &malloc_buffer_2_length, malloc_buffer_2_max_length);
}
else
{
rval= pgfindlib_source_scan(ld, buffer, &buffer_length, tokener_number,
buffer_max_length, lib, platform, origin,
&row_number,
inode_list, &inode_count, &inode_warning_count,
tokener_list,
malloc_buffer_1, &malloc_buffer_1_length, malloc_buffer_1_max_length,
malloc_buffer_2, &malloc_buffer_2_length, malloc_buffer_2_max_length,
program_e_machine);
}
if (rval == PGFINDLIB_MALLOC_BUFFER_1_OVERFLOW)
{
free(malloc_buffer_1); malloc_buffer_1= NULL;
free(malloc_buffer_2); malloc_buffer_2= NULL;
malloc_buffer_1_max_length+= 1000;
goto repeat_malloc;
}
if (rval == PGFINDLIB_MALLOC_BUFFER_2_OVERFLOW)
{
free(malloc_buffer_1); malloc_buffer_1= NULL;
free(malloc_buffer_2); malloc_buffer_2= NULL;
malloc_buffer_2_max_length+= 1000;
goto repeat_malloc;
}
if (rval != PGFINDLIB_OK) PGFINDLIB_FREE_AND_RETURN
}
qsort(malloc_buffer_2, malloc_buffer_2_length, sizeof(char *), pgfindlib_qsort_compare);
/*
Phase 1 complete. At this point, we seem to have a sorted list of all the paths.
In Phase 2, we must dump the paths into the output buffer along with the warnings.
todo: malloc an inode_list about equal to malloc_buffer_2 i.e. # of rows (but more needed for comments I guess)
the problem is that even comments look at node, and they could come before this
todo: with a large number of sources, > 127 - 32, and signed char, the sort order might become wrong
*/
rval= PGFINDLIB_OK;
#if (PGFINDLIB_INCLUDE_ROW_SOURCE_NAME == 1)
int token_number_of_last_source= 0;
#endif
for (unsigned int i= 0; i < malloc_buffer_2_length; ++i)
{
const char *item= malloc_buffer_2[i];
/* First char is source number + 32 */
int token_number_of_source= *item - 32;
#if (PGFINDLIB_INCLUDE_ROW_SOURCE_NAME == 1)
for (unsigned int j= token_number_of_last_source;; ++j)
{
unsigned short int type= tokener_list[j].tokener_comment_id;
if (type == PGFINDLIB_TOKEN_END) break;
if ((type < PGFINDLIB_TOKEN_SOURCE_LD_AUDIT) || (type > PGFINDLIB_TOKEN_SOURCE_NONSTANDARD)) continue;
if (j > token_number_of_source) break;
if ((j > token_number_of_last_source) && (j <= token_number_of_source))
{
char source_name[64];
unsigned short int len= tokener_list[j].tokener_length;
if (len > 64 - 1) len= 64 - 1;
memcpy(source_name, tokener_list[j].tokener_name, len);
source_name[len]= '\0';
rval= pgfindlib_row_source_name(buffer, &buffer_length, buffer_max_length, &row_number,
inode_list, &inode_count, source_name);
if (rval != PGFINDLIB_OK) break;
}
}
if (rval != PGFINDLIB_OK) break;
token_number_of_last_source= token_number_of_source + 1;
#endif
rval= pgfindlib_file(buffer, &buffer_length, item + 1, buffer_max_length, &row_number,
inode_list, &inode_count, &inode_warning_count, tokener_list[token_number_of_source], program_e_machine);
if (rval != PGFINDLIB_OK) break;
}
#if (PGFINDLIB_INCLUDE_ROW_SOURCE_NAME == 1)
if (rval == PGFINDLIB_OK)
{
/* todo: repetitious */
for (unsigned int j= token_number_of_last_source;; ++j)
{
unsigned short int type= tokener_list[j].tokener_comment_id;
if (type == PGFINDLIB_TOKEN_END) break;
if ((type < PGFINDLIB_TOKEN_SOURCE_LD_AUDIT) || (type > PGFINDLIB_TOKEN_SOURCE_NONSTANDARD)) continue;
{
char source_name[64];
unsigned short int len= tokener_list[j].tokener_length;
if (len > 64 - 1) len= 64 - 1;
memcpy(source_name, tokener_list[j].tokener_name, len);
source_name[len]= '\0';
rval= pgfindlib_row_source_name(buffer, &buffer_length, buffer_max_length, &row_number,
inode_list, &inode_count, source_name);
if (rval != PGFINDLIB_OK) break;
}
}
}
#endif
/* free_and_return: */
if (malloc_buffer_1 != NULL) { free(malloc_buffer_1); }
if (malloc_buffer_2 != NULL) { free(malloc_buffer_2); }
return rval;
}
int pgfindlib_strcat(char *buffer, unsigned int *buffer_length, const char *line, unsigned int buffer_max_length)
{
unsigned int line_length= strlen(line);
const char *pointer_to_line= line;
while (*pointer_to_line == ' ') {++pointer_to_line; --line_length; } /* skip lead spaces */
while ((line_length > 0) && (*(pointer_to_line + line_length - 1) == ' ')) --line_length; /* skip trail spaces */
if (*buffer_length + line_length > buffer_max_length) return PGFINDLIB_ERROR_BUFFER_MAX_LENGTH_TOO_SMALL;
memcpy(buffer + *buffer_length, pointer_to_line, line_length);
*buffer_length+= line_length;
*(buffer + *buffer_length)= '\0';
return PGFINDLIB_OK;
}
/*
Compare line to each of the items in statement. If match for number-of-characters-in-soname: 1 true. Else: 0 false.
Assume that line cannot contain ':' and ':' within statement is a delimiter. (Actually so is ',' the way tokenize works.)
Ignore lead or trail spaces.
Treat \n as end of line and ignore it.
We have already checked that strlen(each soname) is <= PGFINDLIB_MAX_PATH_LENGTH.
Beware: ldconfig -p lines start with a control character (tab?).
*/
int pgfindlib_find_line_in_statement(const struct tokener tokener_list[], const char *line)
{
const char *pointer_to_line= line;
while (*pointer_to_line <= ' ') ++pointer_to_line; /* skip lead spaces (or control characters!) in line */
unsigned int line_length= strlen(pointer_to_line);
if ((line_length > 1) && (pointer_to_line[line_length - 1] == '\n')) --line_length; /* skip trail \n */
while ((line_length > 0) && (pointer_to_line[line_length - 1] == ' ')) --line_length; /* skip trail spaces */
if (line_length == 0) return 0; /* false */
for (unsigned int tokener_number= 0; ; ++tokener_number) /* for each source in source name list */
{
int comment_number;
comment_number= tokener_list[tokener_number].tokener_comment_id;
if (comment_number == PGFINDLIB_TOKEN_END) break;
if (comment_number != PGFINDLIB_TOKEN_FILE) continue;
const char *pointer_to_statement= tokener_list[tokener_number].tokener_name;
unsigned soname_length= tokener_list[tokener_number].tokener_length;
if (memcmp(pointer_to_line, pointer_to_statement, soname_length) == 0)
{
return 1; /* true */
}
}
return 0; /* false (though actually we shouldn't get this far) */
}
/*
Pass: library or file name
Do: replace with same library or file name, except that $ORIGIN or $LIB or $PLATFORM is replaced
Return: rval
Todo: It seems loader replaces $LIB or $LIB/ but not $LIB in $LIBxxx, this replaces $LIB in $LIBxxx too.
Todo: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=187114 suggests no need to check DF_ORIGIN flag nowadays
but that hasn't been tested
*/
int pgfindlib_replace_lib_or_platform_or_origin(char *one_library_or_file, unsigned int *replacements_count, const char *lib, const char *platform, const char *origin)
{
*replacements_count= 0;
if (strchr(one_library_or_file, '$') == NULL) return PGFINDLIB_OK;
char buffer_for_output[PGFINDLIB_MAX_PATH_LENGTH*2 + 1];
char *p_line_out= &buffer_for_output[0];
*p_line_out= '\0';
for (const char *p_line_in= one_library_or_file; *p_line_in != '\0';)
{
if ((strncmp(p_line_in, "$ORIGIN", 7) == 0) || (strncmp(p_line_in, "${ORIGIN}", 9) == 0))
{
strcpy(p_line_out, origin);
p_line_out+= strlen(origin);
if (*(p_line_in + 1) == '{') p_line_in+= 9 - 7;
p_line_in+= 7;
++*replacements_count;
}
else if ((strncmp(p_line_in, "$LIB", 4) == 0) || (strncmp(p_line_in, "${LIB}", 6) == 0))
{
strcpy(p_line_out, lib);
p_line_out+= strlen(lib);
if (*(p_line_in + 1) == '{') p_line_in+= 6 - 4;
p_line_in+= 4;
++*replacements_count;
}
else if ((strncmp(p_line_in, "$PLATFORM", 9) == 0) || (strncmp(p_line_in, "${PLATFORM}", 11) == 0))
{
strcpy(p_line_out, platform);
p_line_out+= strlen(platform);
if (*(p_line_in + 1) == '{') p_line_in+= 11 - 9;
p_line_in+= 9;
++*replacements_count;
}
else
{
*p_line_out= *p_line_in;
++p_line_out;
++p_line_in;
}
if ((p_line_out - &buffer_for_output[0]) > PGFINDLIB_MAX_PATH_LENGTH)
return PGFINDLIB_ERROR_MAX_PATH_LENGTH_TOO_SMALL;
*p_line_out= '\0';
}
strcpy(one_library_or_file, buffer_for_output);
return PGFINDLIB_OK;
}
/*
Return: what would loader change "$LIB" or "$PLATFORM" or "$ORIGIN" to
Re method: Use a dummy utility, preferably bin/true, ls should also work but isn't as good
(any program anywhere will work provided it requires any .so, and libc.so is such).
First attempt: read ELF to get "ELF interpreter" i.e. loader name,
which probably is /lib64/ld-linux-x86-64.so.2 or /lib/ld-linux.so.2,
then use it with LD_DEBUG to see search_path when executing the dummy utility.
If that fails: same idea but just running the dummy.
If that fails: (lib) lib64 for 64-bit, lib for 32-bit. (platform) uname -m.
FreeBSD is different.
Todo: get PT_DYNAMIC the way we get PT_INTERN, instead of depending on extern _DYNAMIC (but this is hard, see read_elf()
Todo: If there is a /tls (thread local storage) subdirectory or an x86_64 subdirectory,
search all. But show library search path=main, main-tls, main/x86_64
Todo: If dynamic loader is not the usual e.g. due to "-Wl,-I/tmp/my_ld.so" then add a comment.
*/
int pgfindlib_get_origin_and_lib_and_platform(char *origin, char *lib, char *platform,
char *buffer, unsigned int *buffer_length, unsigned int buffer_max_length,
int *program_e_machine, unsigned int *row_number,
ino_t inode_list[], unsigned int *inode_count)
{
int platform_change_count= 0;
int rval= PGFINDLIB_OK;
int lib_change_count= 0;
#ifdef PGFINDLIB_FREEBSD
int aux_info_return= elf_aux_info(AT_EXECPATH, origin, PGFINDLIB_MAX_PATH_LENGTH);
if (aux_info_return != 0)
{
#if (PGFINDLIB_COMMENT_ELF_AUX_INFO_FAILED != 0)
rval= pgfindlib_comment_is_row("elf_aux_info failed so $ORIGIN is unknown",
PGFINDLIB_COMMENT_ELF_AUX_INFO_FAILED,
buffer, buffer_length, buffer_max_length, row_number, inode_list, inode_count);
if (rval != PGFINDLIB_OK) return rval;
#endif
strcpy(origin, "");
}
strcpy(lib, "lib");
/* platform should be set after the if/else/endif */
#else
{
int readlink_return;
readlink_return= readlink("/proc/self/exe", origin, PGFINDLIB_MAX_PATH_LENGTH);
if ((readlink_return < 0) || (readlink_return >= PGFINDLIB_MAX_PATH_LENGTH))
{
#if (PGFINDLIB_COMMENT_READLINK_FAILED != 0)
rval= pgfindlib_comment_is_row("readlink failed so $ORIGIN is unknown",
PGFINDLIB_COMMENT_READLINK_FAILED,
buffer, buffer_length, buffer_max_length, row_number, inode_list, inode_count);
if (rval != PGFINDLIB_OK) return rval;
#endif
strcpy(origin, "");
}
else *(origin + readlink_return)= '\0';
for (char *p= origin + strlen(origin); p != origin; --p)
{
if (*p == '/')
{
*p= '\0';
break;
}
}
}
#endif /* ifdef PGFINDLIB_FREEBSD */
#if (PGFINDLIB_IF_GET_LIB_OR_PLATFORM != 0)
/* utility name */
/* FreeBSD probably won't have /bin/true, it seems to be a Linux thing, but maybe it will have id */
char utility_name[256]= ""; /* todo: change to const char * */
if (access("/bin/true", X_OK) == 0) strcpy(utility_name, "/bin/true");
else if (access("/bin/cp", X_OK) == 0) strcpy(utility_name, "/bin/cp");
else if (access("/usr/bin/true", X_OK) == 0) strcpy(utility_name, "/usr/bin/true");
else if (access("/usr/bin/cp", X_OK) == 0) strcpy(utility_name, "/usr/bin/cp");
else if (access("/bin/id", X_OK) == 0) strcpy(utility_name, "/bin/id");
else if (access("/usr/bin/id", X_OK) == 0) strcpy(utility_name, "/usr/bin/id");
#if (PGFINDLIB_COMMENT_NO_TRUE_OR_CP != 0)
if (strcmp(utility_name, "") == 0)
{
rval= pgfindlib_comment_is_row("no access to [/usr]/bin/true or [/usr]/bin/cp or /bin/id",
PGFINDLIB_COMMENT_NO_TRUE_OR_CP,
buffer, buffer_length, buffer_max_length, row_number, inode_list, inode_count);
if (rval != PGFINDLIB_OK) return rval;
}
#endif
/* todo: move this up, and if it's zilch then we cannot read elf here */
/* This was void* but that triggered -Warray-bounds during an rpm build. */
extern ElfW(Ehdr) __executable_start;
ElfW(Ehdr)*ehdr= NULL;
ehdr= (ElfW(Ehdr)*) &__executable_start; /* linker e.g. ld.bfd or ld.lld is supposed to add this */
if (ehdr != NULL)
{
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0)
{
#if (PGFINDLIB_COMMENT_EHDR_IDENT != 0)
rval= pgfindlib_comment_is_row("ehdr->ident not valid",
PGFINDLIB_COMMENT_EHDR_IDENT,
buffer, buffer_length, buffer_max_length, row_number, inode_list, inode_count);
if (rval != PGFINDLIB_OK) return rval;
#endif
ehdr= NULL;
}
}
if (ehdr != NULL)
{
*program_e_machine= ehdr->e_machine;
}
else *program_e_machine= 0;
/* first attempt */
const char *dynamic_loader_name= NULL;
if (ehdr != NULL)
{
const char *cc= (char *)ehdr; /* offsets are in bytes so I prefer to use a byte pointer */
cc+= ehdr->e_phoff; /* -> start of program headers */
ElfW(Phdr)*phdr;
for (unsigned int i= 0; i < ehdr->e_phnum; ++i) /* loop through program headers */
{
phdr= (ElfW(Phdr)*)cc;
if (phdr->p_type == PT_INTERP) /* i.e. ELF interpreter */
{
char *cc2= (char *)ehdr;
cc2+= phdr->p_offset;
dynamic_loader_name= cc2;
break;
}
cc+= ehdr->e_phentsize;
}
}
if (dynamic_loader_name == NULL)
{
if ((sizeof(void*)) == 8) dynamic_loader_name= "/lib64/ld-linux-x86-64.so.2"; /* make some gcc/glibc assumptions */
else dynamic_loader_name= "/lib/ld-linux.so.2";
#if (PGFINDLIB_COMMENT_CANT_FIND_DYNAMIC_LOADER != 0)
char comment[512];
sprintf(comment, "can't get ehdr dynamic loader so assume %s", dynamic_loader_name);
rval= pgfindlib_comment_is_row(comment,
PGFINDLIB_COMMENT_CANT_FIND_DYNAMIC_LOADER,
buffer, buffer_length, buffer_max_length, row_number, inode_list, inode_count);
if (rval != PGFINDLIB_OK) return rval;
#endif
}
if (access(dynamic_loader_name, X_OK) != 0)
{
#if (PGFINDLIB_COMMENT_CANT_ACCESS_DYNAMIC_LOADER != 0)
char comment[512];
sprintf(comment, "can't access %s", dynamic_loader_name);
rval= pgfindlib_comment_is_row(comment,
PGFINDLIB_COMMENT_CANT_ACCESS_DYNAMIC_LOADER,
buffer, buffer_length, buffer_max_length, row_number, inode_list, inode_count);
if (rval != PGFINDLIB_OK) return rval;
#endif
dynamic_loader_name= NULL;
}
if (dynamic_loader_name != NULL)
{
#define REPLACEE_IS_LIB 0
#define REPLACEE_IS_PLATFORM 1
for (int i= REPLACEE_IS_LIB; i <= REPLACEE_IS_PLATFORM; ++i) /* 0 means "$LIB", 1 means "$PLATFORM" */
{
char replacee[16];
if (i == REPLACEE_IS_LIB) strcpy(replacee, "$LIB");
else strcpy(replacee, "$PLATFORM");
int change_count= 0;
FILE *fp;
char popen_arg[PGFINDLIB_MAX_PATH_LENGTH + 1];
sprintf(popen_arg,
"env -u LD_DEBUG_OUTPUT LD_LIBRARY_PATH='/PRE_OOKPIK/%s/POST_OOKPIK' LD_DEBUG=libs %s --inhibit-cache %s 2>/dev/stdout",
replacee, dynamic_loader_name, utility_name);
fp= popen(popen_arg, "r");
if (fp != NULL)
{
char buffer_for_ookpik[PGFINDLIB_MAX_PATH_LENGTH + 1];
while (fgets(buffer_for_ookpik, sizeof(buffer_for_ookpik), fp) != NULL)
{
const char *pre_ookpik= strstr(buffer_for_ookpik, "PRE_OOKPIK/");
if (pre_ookpik == NULL) continue;
const char *post_ookpik= strstr(pre_ookpik, "POST_OOKPIK");
if (post_ookpik == NULL) continue;
pre_ookpik+= strlen("PRE_OOKPIK/");
unsigned int len= post_ookpik - (pre_ookpik + 1);
if (i == REPLACEE_IS_LIB) { memcpy(lib, pre_ookpik, len); *(lib + len)= '\0'; ++lib_change_count; }
else { memcpy(platform, pre_ookpik, len); *(platform + len)= '\0'; ++platform_change_count; }
++change_count;
break;
}
pclose(fp);
}
/* second attempt */
if (change_count == 0)
{
sprintf(popen_arg,
"env -u LD_DEBUG_OUTPUT LD_LIBRARY_PATH='/PRE_OOKPIK/%s/POST_OOKPIK' LD_DEBUG=libs %s 2>/dev/stdout",
replacee, utility_name);
fp= popen(popen_arg, "r");
if (fp != NULL)
{
char buffer_for_ookpik[PGFINDLIB_MAX_PATH_LENGTH + 1];
while (fgets(buffer_for_ookpik, sizeof(buffer_for_ookpik), fp) != NULL)
{
const char *pre_ookpik= strstr(buffer_for_ookpik, "PRE_OOKPIK/");
if (pre_ookpik == NULL) continue;
const char *post_ookpik= strstr(pre_ookpik, "POST_OOKPIK");
if (post_ookpik == NULL) continue;
pre_ookpik+= strlen("PRE_OOKPIK/");
unsigned int len= post_ookpik - (pre_ookpik + 1);
if (i == REPLACEE_IS_LIB) { memcpy(lib, pre_ookpik, len); *(lib + len)= '\0'; ++lib_change_count; }
else { memcpy(platform, pre_ookpik, len); *(platform + len)= '\0'; ++platform_change_count; }
break;
}
pclose(fp);
}
}
}
}
#endif /* if (PGFINDLIB_IF_GET_LIB_OR_PLATFORM != 0) */
if (lib_change_count == 0)
{
if ((sizeof(void*)) == 8) strcpy(lib, "lib64"); /* default $LIB if pgfindlib__get_lib_or_platform doesn't work */
else strcpy(lib, "lib");
#if (PGFINDLIB_COMMENT_ASSUMING_LIB != 0)
char comment[5000];
sprintf(comment, "assuming $LIB is %s", lib);
rval= pgfindlib_comment_is_row(comment,
PGFINDLIB_COMMENT_ASSUMING_LIB,
buffer, buffer_length, buffer_max_length, row_number, inode_list, inode_count);
if (rval != PGFINDLIB_OK) return rval;
#endif
}
if (platform_change_count == 0)
{
char buffer_for_replacement[PGFINDLIB_MAX_PATH_LENGTH + 1]= "?";
FILE *fp= popen("LD_LIBRARY_PATH= LD_DEBUG= LD_PRELOAD= uname -m 2>/dev/null", "r");
if (fp != NULL)
{
if (fgets(buffer_for_replacement, sizeof(buffer_for_replacement), fp) == NULL)
{;}
pclose(fp);
}
#if (PGFINDLIB_COMMENT_UNAME_FAILED != 0)
if (strcmp(buffer_for_replacement, "?") == 0)
{
rval= pgfindlib_comment_is_row("uname -m failed",
PGFINDLIB_COMMENT_UNAME_FAILED,
buffer, buffer_length, buffer_max_length, row_number, inode_list, inode_count);
if (rval != PGFINDLIB_OK) return rval;
}
#endif
char *pointer_to_n= strchr(buffer_for_replacement, '\n');
if (pointer_to_n != NULL) *pointer_to_n= '\0';
strcpy(platform, buffer_for_replacement);
#if (PGFINDLIB_COMMENT_ASSUMING_PLATFORM != 0)
char comment[5000];
sprintf(comment, "assuming $PLATFORM is %s", platform);
rval= pgfindlib_comment_is_row(comment,
PGFINDLIB_COMMENT_ASSUMING_PLATFORM,
buffer, buffer_length, buffer_max_length, row_number, inode_list, inode_count);
if (rval != PGFINDLIB_OK) return rval;
#endif
}
return rval; /* which is doubtless PGFINDLIB_OK */
}
/*
*For a comment column
Put column of row in buffer.
All warnings and comments must have form: 3-decimal-digit-number text delimiter
The number should never change, the text should rarely change.
Todo: flag to ignore column because column_number
flag to exclude text and only show number
limit text length
enclose in ''s or ""s if contains delimiter
*/
int pgfindlib_comment_in_row(char *comment, unsigned int comment_number, int additional_number)
{
const char *text;
if (comment_number == PGFINDLIB_COMMENT_DUPLICATE) sprintf(comment, "%03d %s %d", comment_number, "duplicate of", additional_number);
else
{
if (comment_number == PGFINDLIB_COMMENT_ACCESS_FAILED) text= "access(filename, R_OK) failed";
if (comment_number == PGFINDLIB_COMMENT_LSTAT_FAILED) text= "lstat(filename) failed";
if (comment_number == PGFINDLIB_COMMENT_SYMLINK) text= "symlink";
if (comment_number == PGFINDLIB_COMMENT_MAX_INODE_COUNT_TOO_SMALL) text= "MAX_INODE_COUNT is too small";
if (comment_number == PGFINDLIB_COMMENT_ELF_OPEN_FAILED) text= "elf open failed";
if (comment_number == PGFINDLIB_COMMENT_ELF_READ_FAILED) text= "elf read failed";
if (comment_number == PGFINDLIB_COMMENT_ELF_HAS_INVALID_IDENT) text= "elf has invalid ident";
if (comment_number == PGFINDLIB_COMMENT_ELF_IS_NOT_EXEC_OR_DYN) text= "elf is not exec or dyn";
if (comment_number == PGFINDLIB_COMMENT_ELF_SHT_DYNAMIC_NOT_FOUND) text= "elf sht_dynamic not found";
if (comment_number == PGFINDLIB_COMMENT_ELF_MACHINE_DOES_NOT_MATCH) text= "elf machine does not match";
sprintf(comment, "%03d %s", comment_number, text);
}
return PGFINDLIB_OK;
}
/* This can be called when a warning|error will be the only item in the row, e.g. when about to return syntax error
But notice the fixed size of comment_with_number, don't call unless it's certain that the message is short
*/
int pgfindlib_comment_is_row(const char *comment, unsigned int comment_number,
char *buffer, unsigned int *buffer_length, unsigned int buffer_max_length, unsigned int *row_number,
ino_t inode_list[], unsigned int *inode_count)
{
if (*inode_count != PGFINDLIB_MAX_INODE_COUNT) { inode_list[*inode_count]= -1; ++*inode_count; }
const char *columns_list[MAX_COLUMNS_PER_ROW];
for (int i= 0; i < MAX_COLUMNS_PER_ROW; ++i) columns_list[i]= "";
char comment_with_number[256];
sprintf(comment_with_number, "%03d %s", comment_number, comment);
columns_list[COLUMN_FOR_COMMENT_1]= comment_with_number;
return pgfindlib_row_bottom_level(buffer, buffer_length, buffer_max_length, row_number, columns_list);
}
/*
called "bottom level" because ultimately all pgfindlib_row_* functions should call here
todo: move overflow check to here
*/
int pgfindlib_row_bottom_level(char *buffer, unsigned int *buffer_length, unsigned int buffer_max_length, unsigned int *row_number,
const char *columns_list[])
{
int rval;
char row_number_string[8];
unsigned int buffer_length_save= *buffer_length;
for (int i= 0; i < MAX_COLUMNS_PER_ROW; ++i)
{
if (i == COLUMN_FOR_ROW_NUMBER)
{
sprintf(row_number_string, "%d", *row_number);
rval= pgfindlib_strcat(buffer, buffer_length, row_number_string, buffer_max_length);
}
else rval= pgfindlib_strcat(buffer, buffer_length, columns_list[i], buffer_max_length);
if (rval != PGFINDLIB_OK) goto overflow;
rval= pgfindlib_strcat(buffer, buffer_length, PGFINDLIB_COLUMN_DELIMITER, buffer_max_length); /* ":" */
if (rval != PGFINDLIB_OK) goto overflow;
}
rval= pgfindlib_strcat(buffer, buffer_length, PGFINDLIB_ROW_DELIMITER, buffer_max_length); /* "\n" */
if (rval != PGFINDLIB_OK) goto overflow;
++*row_number;
return rval;
overflow:
/* todo: this should be a row and there should be a guarantee that it will fit i.e. the regular strcat check is buffer_length - what's needed for overflow message */
*buffer_length= buffer_length_save;
pgfindlib_strcat(buffer, buffer_length, "OVFLW", buffer_max_length);
return rval; /* i.e. return the rval that caused overflow */
}
static int pgfindlib_row_version(char *buffer, unsigned int *buffer_length, unsigned buffer_max_length, unsigned int *row_number, ino_t inode_list[], unsigned int *inode_count)
{
if (*inode_count != PGFINDLIB_MAX_INODE_COUNT) { inode_list[*inode_count]= -1; ++*inode_count; }
char row_program[64];
sprintf(row_program, "%03d pgfindlib", PGFINDLIB_COMMENT_PGFINDLIB);
char row_version[64];
sprintf(row_version, "%03d version %d.%d.%d", PGFINDLIB_COMMENT_VERSION, PGFINDLIB_VERSION_MAJOR, PGFINDLIB_VERSION_MINOR, PGFINDLIB_VERSION_PATCH);
char row_url[64];
sprintf(row_url, "%03d https://github.com/pgulutzan/pgfindlib", PGFINDLIB_COMMENT_URL);
const char *columns_list[MAX_COLUMNS_PER_ROW];
for (int i= 0; i < MAX_COLUMNS_PER_ROW; ++i) columns_list[i]= "";
columns_list[COLUMN_FOR_COMMENT_1]= row_program;
columns_list[COLUMN_FOR_COMMENT_2]= row_version;
columns_list[COLUMN_FOR_COMMENT_3]= row_url;
return pgfindlib_row_bottom_level(buffer, buffer_length, buffer_max_length, row_number, columns_list);
}
int pgfindlib_row_lib(char *buffer, unsigned int *buffer_length, unsigned buffer_max_length, unsigned int *row_number,
ino_t inode_list[], unsigned int *inode_count,
const char *lib, const char *platform, const char *origin)
{
if (*inode_count != PGFINDLIB_MAX_INODE_COUNT) { inode_list[*inode_count]= -1; ++*inode_count; }
char column_lib[PGFINDLIB_MAX_PATH_LENGTH + 100];
char column_platform[PGFINDLIB_MAX_PATH_LENGTH + 100];
char column_origin[PGFINDLIB_MAX_PATH_LENGTH + 100];
sprintf(column_lib, "%03d $LIB=%s", PGFINDLIB_COMMENT_LIB_STRING, lib);
sprintf(column_platform, "%03d $PLATFORM=%s", PGFINDLIB_COMMENT_PLATFORM_STRING, platform);
sprintf(column_origin, "%03d $ORIGIN=%s", PGFINDLIB_COMMENT_ORIGIN_STRING, origin);
const char *columns_list[MAX_COLUMNS_PER_ROW];
for (int i= 0; i < MAX_COLUMNS_PER_ROW; ++i) columns_list[i]= "";
columns_list[COLUMN_FOR_COMMENT_1]= column_lib;
columns_list[COLUMN_FOR_COMMENT_2]= column_platform;
columns_list[COLUMN_FOR_COMMENT_3]= column_origin;
return pgfindlib_row_bottom_level(buffer, buffer_length, buffer_max_length, row_number, columns_list);
}
/* Dump source name as a row-level comment. By default PGFINDLIB_INCLUDE_ROW_SOURCE_NAME == 0 so this is disabled */
int pgfindlib_row_source_name(char *buffer, unsigned int *buffer_length, unsigned buffer_max_length, unsigned int *row_number,
ino_t inode_list[], unsigned int *inode_count,
const char *source_name)
{
#if (PGFINDLIB_INCLUDE_ROW_SOURCE_NAME == 1)
if (*inode_count != PGFINDLIB_MAX_INODE_COUNT) { inode_list[*inode_count]= -1; ++*inode_count; }
const char *columns_list[MAX_COLUMNS_PER_ROW];
for (int i= 0; i < MAX_COLUMNS_PER_ROW; ++i) columns_list[i]= "";
columns_list[COLUMN_FOR_SOURCE]= source_name;
return pgfindlib_row_bottom_level(buffer, buffer_length, buffer_max_length, row_number, columns_list);
#else
(void) buffer; (void) buffer_length; (void) buffer_max_length; (void) row_number; (void) inode_list; (void) inode_count; (void) source_name;
return PGFINDLIB_OK;
#endif
}
/*
Put file name in buffer. Precede with, or include, comments if there are any.
todo: pgfindlib_comment = source name if first in source and not done before
todo: some parameters unused e.g. inode_warning_count
todo: can use st_nlink to see how many hardlinks a file has (expect it to have at least 1)
todo: maybe there's a way to follow a symlink -- we use lstat on the file, maybe stat() would do better
*/
int pgfindlib_file(char *buffer, unsigned int *buffer_length, const char *line, unsigned int buffer_max_length,
unsigned int *row_number,
ino_t inode_list[], unsigned int *inode_count, unsigned int *inode_warning_count,
struct tokener tokener_list_item,
int program_e_machine)
{
(void) inode_warning_count;
char line_copy[PGFINDLIB_MAX_PATH_LENGTH + 1]; /* todo: change following to "get rid of trailing \n" */
{
int i= 0; int j= 0;
for (;;)
{
if (*(line + i) != '\n') { *(line_copy + j)= *(line + i); ++j; }
if (*(line + i) == '\0') break;
++i;
}
}
const char *columns_list[MAX_COLUMNS_PER_ROW];
for (int i= 0; i < MAX_COLUMNS_PER_ROW; ++i) columns_list[i]= "";
unsigned int columns_list_number= COLUMN_FOR_COMMENT_1;
char warning_max_inode_count_is_too_small[64]= ""; /* Do not move warnings into "if ... {} ...", columns_list needs their addresses */
char warning_lstat_failed[64]= "";
char warning_access_failed[64]= "";
char warning_symlink[64]= "";
char warning_duplicate[64]= "";
char warning_elf[64]= "";
if (access(line_copy, R_OK) != 0) /* It's poorly documented but tests indicate X_OK doesn't matter and R_OK matters */
{
#if (PGFINDLIB_COMMENT_ACCESS_FAILED != 0)
pgfindlib_comment_in_row(warning_access_failed, PGFINDLIB_COMMENT_ACCESS_FAILED, 0);
columns_list[columns_list_number++]= warning_access_failed;
#endif
}
ino_t inode;
struct stat sb;
if (lstat(line_copy, &sb) == -1)
{
#if (PGFINDLIB_COMMENT_LSTAT_FAILED != 0)
pgfindlib_comment_in_row(warning_lstat_failed, PGFINDLIB_COMMENT_LSTAT_FAILED, 0);
columns_list[columns_list_number++]= warning_lstat_failed;
#endif
inode= -1; /* a dummy so count of inodes is right but this won't be found later */
}
else
{
mode_t st_mode= sb.st_mode & S_IFMT;
if ((st_mode !=S_IFREG) && (st_mode != S_IFLNK)) return PGFINDLIB_OK; /* not file or symlink so not candidate */
/* Here, if (st_mode == S_IFLNK) and include_symlinks is off, return */
/* Here, if (duplicate) and include duplicates is off, return */
if (st_mode == S_IFLNK)
{
#if (PGFINDLIB_COMMENT_SYMLINK != 0)
/* todo: find out: symlink of what? */
pgfindlib_comment_in_row(warning_symlink, PGFINDLIB_COMMENT_SYMLINK, 0);
columns_list[columns_list_number++]= warning_symlink;
#endif
}
inode= sb.st_ino;
for (unsigned int i= 0; i < *inode_count; ++i)
{
if (inode_list[i] == inode)
{
#if (PGFINDLIB_COMMENT_DUPLICATE != 0)
pgfindlib_comment_in_row(warning_duplicate, PGFINDLIB_COMMENT_DUPLICATE, i + 1); /* "+ 1" because row_number starts at 1 */
columns_list[columns_list_number++]= warning_duplicate;
#endif
break;
}
}
}
{
struct tokener tokener_list[1]; /* won't be used */
int elf_rval= pgfindlib_read_elf(tokener_list, line_copy, PGFINDLIB_REASON_SO_CHECK, program_e_machine);
if (elf_rval != 0)
{
int unknown_failures= 0;
/* These all have the same warning_elf so suppressing with "#if ... #endif" would be complicated. */
if (elf_rval == PGFINDLIB_COMMENT_ELF_OPEN_FAILED)
pgfindlib_comment_in_row(warning_elf, PGFINDLIB_COMMENT_ELF_OPEN_FAILED, 0);
else if (elf_rval == PGFINDLIB_COMMENT_ELF_READ_FAILED)
pgfindlib_comment_in_row(warning_elf, PGFINDLIB_COMMENT_ELF_READ_FAILED, 0);
else if (elf_rval == PGFINDLIB_COMMENT_ELF_HAS_INVALID_IDENT)
pgfindlib_comment_in_row(warning_elf, PGFINDLIB_COMMENT_ELF_HAS_INVALID_IDENT, 0);
else if (elf_rval == PGFINDLIB_COMMENT_ELF_IS_NOT_EXEC_OR_DYN)
pgfindlib_comment_in_row(warning_elf, PGFINDLIB_COMMENT_ELF_IS_NOT_EXEC_OR_DYN, 0);
else if (elf_rval == PGFINDLIB_COMMENT_ELF_SHT_DYNAMIC_NOT_FOUND)
pgfindlib_comment_in_row(warning_elf, PGFINDLIB_COMMENT_ELF_SHT_DYNAMIC_NOT_FOUND, 0);
else if (elf_rval == PGFINDLIB_COMMENT_ELF_MACHINE_DOES_NOT_MATCH)
pgfindlib_comment_in_row(warning_elf, PGFINDLIB_COMMENT_ELF_MACHINE_DOES_NOT_MATCH, 0);
else ++unknown_failures; /* if this happens it's a bug */
if (unknown_failures == 0) columns_list[columns_list_number++]= warning_elf;
}
}
if (*inode_count == PGFINDLIB_MAX_INODE_COUNT)
{
#if (PGFINDLIB_COMMENT_MAX_INODE_COUNT_TOO_SMALL != 0)
pgfindlib_comment_in_row(warning_max_inode_count_is_too_small, PGFINDLIB_COMMENT_MAX_INODE_COUNT_TOO_SMALL, 0);
columns_list[columns_list_number++]= warning_max_inode_count_is_too_small;
#endif
}
else
{
inode_list[*inode_count]= inode;
++*inode_count;