Making a shared data structure in c - c

I have created a data structure in my C program as follows,
typedef struct {
int *array;
size_t used;
size_t size;
} Array;
void initArray(Array *a, size_t initialSize) {
a->array = (int *)malloc(initialSize * sizeof(int));
a->used = 0;
a->size = initialSize;
}
void insertArray(Array *a, int element) {
if (a->used == a->size) {
a->size *= 2;
a->array = (int *)realloc(a->array, a->size * sizeof(int));
}
a->array[a->used++] = element;
}
void freeArray(Array *a) {
free(a->array);
a->array = NULL;
a->used = a->size = 0;
}
Then I'm adding some data to that data structure from an external text file using following method,
Array read_ints (const char* file_name)
{
Array numbers;
initArray(&numbers,5);
FILE* file = fopen (file_name, "r");
int i = 0;
int count = 0;
fscanf (file, "%d,", &i);
insertArray(&numbers,i);
while (!feof (file))
{
//printf ("%d ", i);
fscanf (file, "%d,", &i);
insertArray(&numbers,i);
}
fclose (file);
return numbers;
}
Now what I need to do is, I need to make the 'Array' data structure a shared memory portion so that the both child and parent processes on my program could access that data structure. I have no idea on how to make it a shared memory. I'm aware that the shmget() system call can be used to get a shared memory in UNIX environment. But i cannot see how to use that system call on this scenario. Please help me.

Main code — shm-master.c
#include "posixver.h"
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <unistd.h>
#include "so-stderr.h"
enum { DEFAULT_SHM_SIZE = 65536 };
enum { DEFAULT_FTOK_ID = 0 };
static const char default_filename[] = "/etc/passwd";
static const char usestr[] = "[-adx][-f file][-s size][-i id]";
static const char optstr[] = "adf:s:x";
int main(int argc, char **argv)
{
int aflag = 0;
int xflag = 0;
int dflag = 0;
int id = DEFAULT_FTOK_ID;
size_t size = DEFAULT_SHM_SIZE;
const char *file = default_filename;
int opt;
err_setarg0(argv[0]);
while ((opt = getopt(argc, argv, optstr)) != -1)
{
switch (opt)
{
case 'a':
aflag = 1;
break;
case 'd':
dflag = 1;
break;
case 'f':
file = optarg;
break;
case 'i':
id = atoi(optarg);
break;
case 's':
size = strtoull(optarg, 0, 0);
if (size == 0)
err_error("Invalid size (%s) evaluates to zero\n", optarg);
break;
case 'x':
xflag = 1;
break;
default:
err_usage(usestr);
}
}
if (aflag + dflag + xflag > 1)
err_error("%d of 3 mutually exclusive options -a, -d and -x specified\n", aflag + dflag + xflag);
printf("ID: %d, File: %s\n", id, file);
key_t key = ftok(file, id);
printf("Key: 0x%.8" PRIX64 "\n", (uint64_t)key);
int shmflg = S_IRUSR | S_IWUSR;
if (!aflag && !dflag)
shmflg |= IPC_CREAT;
if (xflag)
shmflg |= IPC_EXCL;
int shmid = shmget(key, size, shmflg);
if (shmid < 0)
err_syserr("Failed to get shared memory ID: ");
printf("ShmID: %d\n", shmid);
if (dflag)
{
struct shmid_ds buf;
int rc = shmctl(shmid, IPC_RMID, &buf);
if (rc < 0)
err_syserr("Failed to delete shared memory: ");
printf("Shared memory removed\n");
}
else
{
void *space = shmat(shmid, 0, 0);
if (space == (void *)-1)
err_syserr("Failed to attach to shared memory: ");
printf("Shared memory allocated at 0x%" PRIXPTR "\n", (uintptr_t)space);
memset(space, '\0', size);
int rc = shmdt(space);
if (rc != 0)
err_syserr("Failed to detach from shared memory: ");
printf("Detached from shared memory\n");
}
return 0;
}
Library code — so-stderr.h
#ifndef SO_STDERR_H_INCLUDED
#define SO_STDERR_H_INCLUDED
extern void err_setarg0(const char *arg0);
extern void err_error(const char *fmt, ...);
extern void err_syserr(const char *fmt, ...);
#endif /* SO_STDERR_H_INCLUDED */
Library code — so-stderr.c
#include "so-stderr.h"
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static const char *argv0 = "**undefined**";
void err_setarg0(const char *arg0)
{
argv0 = arg0;
}
void err_error(const char *fmt, ...)
{
fprintf(stderr, "%s: ", argv0);
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
exit(EXIT_FAILURE);
}
void err_syserr(const char *fmt, ...)
{
int errnum = errno;
fprintf(stderr, "%s: ", argv0);
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "(%d: %s)", errnum, strerror(errnum));
putc('\n', stderr);
exit(EXIT_FAILURE);
}
Configuration header — posixver.h
You can adjust the higher version number to 700 (for POSIX 2008/2013) on many systems, but it probably isn't a good idea on Mac OS X, even with 10.10.3 Yosemite.
#ifndef JLSS_ID_POSIXVER_H
#define JLSS_ID_POSIXVER_H
#if !defined(_XOPEN_SOURCE) && !defined(_POSIX_C_SOURCE)
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#endif
In all cases, the 'real' code includes (a few) comments and other explanations of what's going on. The production stderr.h and stderr.c are more complex than the minimal version shown, but for many purposes, what's shown is equivalent to the production version.
Example run
$ ./shm-master -H
./shm-master: invalid option -- 'H'
Usage: ./shm-master [-adx][-f file][-s size][-i id]
$ ./shm-master -ax
./shm-master: 2 of 3 mutually exclusive options -a, -d and -x specified
$ ./shm-master -dx
./shm-master: 2 of 3 mutually exclusive options -a, -d and -x specified
$ ./shm-master -da
./shm-master: 2 of 3 mutually exclusive options -a, -d and -x specified
$ ./shm-master -dax
./shm-master: 3 of 3 mutually exclusive options -a, -d and -x specified
$ ipcs -m | grep -v '^0x00000000 '
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x620010f7 0 root 660 557920 4
0x63002725 32769 root 666 82164 3
$ ./shm-master -x
ID: 0, File: /etc/passwd
Key: 0x0000009F
ShmID: 44793901
Shared memory allocated at 0x7F29AC43A000
Detached from shared memory
$ ipcs -m | grep -v '^0x00000000 '
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x620010f7 0 root 660 557920 4
0x63002725 32769 root 666 82164 3
0x0000009f 44793901 jleffler 600 65536 0
$ ./shm-master -d
ID: 0, File: /etc/passwd
Key: 0x0000009F
ShmID: 44793901
Shared memory removed
$ ipcs -m
$ grep -v '^0x00000000 '
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x620010f7 0 root 660 557920 4
0x63002725 32769 root 666 82164 3
$ ./shm-master -f /home/jleffler/soq/shm-master -a
./shm-master: Failed to get shared memory ID: (2: No such file or directory)
ID: 0, File: /home/jleffler/soq/shm-master
Key: 0x00010FB9
$ ./shm-master -f /home/jleffler/soq/shm-master -d
./shm-master: Failed to get shared memory ID: (2: No such file or directory)
ID: 0, File: /home/jleffler/soq/shm-master
Key: 0x00010FB9
$ ./shm-master -f /home/jleffler/soq/shm-master -x
ID: 0, File: /home/jleffler/soq/shm-master
Key: 0x00010FB9
ShmID: 44826669
Shared memory allocated at 0x7FA1488CA000
Detached from shared memory
$ ./shm-master -f /home/jleffler/soq/shm-master -d
ID: 0, File: /home/jleffler/soq/shm-master
Key: 0x00010FB9
ShmID: 44826669
Shared memory removed
$ ./shm-master -f /home/jleffler/soq/shm-master -x
ID: 0, File: /home/jleffler/soq/shm-master
Key: 0x00010FB9
ShmID: 44859437
Shared memory allocated at 0x7F93005EC000
Detached from shared memory
$ shmid=$(./shm-master -f /home/jleffler/soq/shm-master -a sed -n '/ShmID: /s///p')
$ ipcs -m -i $shmid
Shared memory Segment shmid=44859437
uid=199484 gid=5000 cuid=199484 cgid=5000
mode=0600 access_perms=0600
bytes=65536 lpid=31202 cpid=31200 nattch=0
att_time=Fri Apr 17 11:37:06 2015
det_time=Fri Apr 17 11:37:06 2015
change_time=Fri Apr 17 11:37:06 2015
$ ./shm-master -f /home/jleffler/soq/shm-master -d
ID: 0, File: /home/jleffler/soq/shm-master
Key: 0x00010FB9
ShmID: 44859437
Shared memory removed
$
Incidentally, the ipcs option -i id is a Linux extension over the POSIX specification for ipcs, and the option is not available on, for example, Mac OS X (BSD). The nearest equivalent would be something like ipcs -m -a | grep "$shmid", which isn't perfect. The grep -v '^0x00000000 ' operations eliminate the private shared memory segments (there were a lot of them in use on the machine I did the testing on).

Related

List all the hole and data segments in a sparse file

I am trying to implement a program which can print all the hole and data segments in a regular sparse file using lseek(2) and its arguments SEEK_DATA and SEEK_HOLE, which is something like:
$ ./list_hold_and_data_segs sparse_file
This file has 100 bytes
[0, 10]: hole
[11, 99]: data(end)
Implementation
/*
* list_hole_and_data_segs.c
*/
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
enum Type {
HOLE,
DATA,
};
void find_all_holes(int fd);
int main(int ac, char *av[])
{
int fd = open(av[1], O_RDONLY);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
find_all_holes(fd);
return 0;
}
void find_all_holes(int fd)
{
off_t cur_offset = 0; // current offset
enum Type cur_type; // current byte type
off_t file_size = lseek(fd, 0, SEEK_END);
off_t index_of_last_byte = file_size - 1;
printf("This file has %ld bytes\n", file_size);
// check the type of byte 0
off_t res = lseek(fd, 0, SEEK_HOLE);
if (res == 0) {
cur_type = HOLE;
} else if (res == file_size) {
printf("[0, %ld]: data(then exit)\n", index_of_last_byte);
exit(0);
} else {
cur_type = DATA;
cur_offset = res;
}
while (cur_offset <= index_of_last_byte) {
off_t new_offset =lseek(fd, cur_offset,
((cur_type == DATA) ? SEEK_HOLE : SEEK_DATA));
if ((cur_type == HOLE && new_offset == -1 && errno == ENXIO) ||
(cur_type == DATA && new_offset == file_size)) {
// from current position to the end of this file: `cur_type`
printf("[%ld, %ld]: %s(end)\n", cur_offset,
index_of_last_byte,
((cur_type == DATA) ? "data" : "hole"));
break; // exit of while loop
} else {
// from current offset to the new offset: `cur_type`
printf("[%ld, %ld]: %s\n", cur_offset, new_offset - 1,
((cur_type == DATA) ? "data" : "hole"));
cur_offset = new_offset;
cur_type = (cur_type == DATA) ? HOLE : DATA;
}
}
}
Test my implementation
I use the following code snippet to create a sparse file, error handling is omitted for simplicity:
/*
* create_sparse_file.c
*/
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd = open("sparse_file", O_CREAT | O_WRONLY | O_TRUNC, 0666);
lseek(fd, 10000, SEEK_CUR);
write(fd, "HELLO", 5);
close(fd);
return 0;
}
$ gcc create_sparse_file.c -o create_sparse_file && ./create_sparse_file
$ stat sparse_file
File: sparse_file
Size: 10005 Blocks: 8 IO Block: 4096 regular file
Device: 803h/2051d Inode: 3556105 Links: 1
# create a normal file as a comparision
$ cp sparse_file not_sparse_file --sparse=never
$ stat not_sparse_file
File: not_sparse_file
Size: 10005 Blocks: 24 IO Block: 4096 regular file
Device: 803h/2051d Inode: 3557867 Links: 1
$ gcc list_hole_and_data_segs.c -o list_hole_and_data_segs
$ ./list_hole_and_data_segs sparse_file
This file has 10005 bytes
[0, 8191]: hole
[8192, 10004]: data(end)
Question
As you can see, the output of ./list_hole_and_data_seg sparse_file is:
[0, 8191]: hole
[8192, 10004]: data(end)
And the real case is:
[0, 9999]: hole
[10000, 10004]: data(end)
What makes the behavior of list_hole_and_data_seg not consistent with the real case and how to make it correct?
Environment
$ uname -a
Linux pop-os 5.17.15-76051715-generic #202206141358~1655919116~22.04~1db9e34 SMP PREEMPT Wed Jun 22 19 x86_64 x86_64 x86_64 GNU/Linux
$ df -hT .
Filesystem Type Size Used Avail Use% Mounted on
/dev/sda3 ext4 103G 54G 44G 56% /
$ stat -f .
File: "."
ID: 4885eb446c106708 Namelen: 255 Type: ext2/ext3
Block size: 4096 Fundamental block size: 4096
Blocks: Total: 26819732 Free: 12805152 Available: 11431226
Inodes: Total: 6856704 Free: 6062138
$ gcc --version
gcc (Ubuntu 11.2.0-19ubuntu1) 11.2.0
$ ldd --version
ldd (Ubuntu GLIBC 2.35-0ubuntu3) 2.35

Is there a way to determine write protection of a page in Linux [duplicate]

I would like to see Memory layout of my program in C so that i can understand all the different segments of the Memory practically during run-time like change in BSS or Heap for ex ?
In Linux, for process PID, look at /proc/PID/maps and /proc/PID/smaps pseudofiles. (The process itself can use /proc/self/maps and /proc/self/smaps.)
Their contents are documented in man 5 proc.
Here's an example of how you might read the contents into a linked list of address range structures.
mem-stats.h:
#ifndef MEM_STATS_H
#define MEM_STATS_H
#include <stdlib.h>
#include <sys/types.h>
#define PERMS_READ 1U
#define PERMS_WRITE 2U
#define PERMS_EXEC 4U
#define PERMS_SHARED 8U
#define PERMS_PRIVATE 16U
typedef struct address_range address_range;
struct address_range {
struct address_range *next;
void *start;
size_t length;
unsigned long offset;
dev_t device;
ino_t inode;
unsigned char perms;
char name[];
};
address_range *mem_stats(pid_t);
void free_mem_stats(address_range *);
#endif /* MEM_STATS_H */
mem-stats.c:
#define _POSIX_C_SOURCE 200809L
#define _BSD_SOURCE
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "mem-stats.h"
void free_mem_stats(address_range *list)
{
while (list) {
address_range *curr = list;
list = list->next;
curr->next = NULL;
curr->length = 0;
curr->perms = 0U;
curr->name[0] = '\0';
free(curr);
}
}
address_range *mem_stats(pid_t pid)
{
address_range *list = NULL;
char *line = NULL;
size_t size = 0;
FILE *maps;
if (pid > 0) {
char namebuf[128];
int namelen;
namelen = snprintf(namebuf, sizeof namebuf, "/proc/%ld/maps", (long)pid);
if (namelen < 12) {
errno = EINVAL;
return NULL;
}
maps = fopen(namebuf, "r");
} else
maps = fopen("/proc/self/maps", "r");
if (!maps)
return NULL;
while (getline(&line, &size, maps) > 0) {
address_range *curr;
char perms[8];
unsigned int devmajor, devminor;
unsigned long addr_start, addr_end, offset, inode;
int name_start = 0;
int name_end = 0;
if (sscanf(line, "%lx-%lx %7s %lx %u:%u %lu %n%*[^\n]%n",
&addr_start, &addr_end, perms, &offset,
&devmajor, &devminor, &inode,
&name_start, &name_end) < 7) {
fclose(maps);
free(line);
free_mem_stats(list);
errno = EIO;
return NULL;
}
if (name_end <= name_start)
name_start = name_end = 0;
curr = malloc(sizeof (address_range) + (size_t)(name_end - name_start) + 1);
if (!curr) {
fclose(maps);
free(line);
free_mem_stats(list);
errno = ENOMEM;
return NULL;
}
if (name_end > name_start)
memcpy(curr->name, line + name_start, name_end - name_start);
curr->name[name_end - name_start] = '\0';
curr->start = (void *)addr_start;
curr->length = addr_end - addr_start;
curr->offset = offset;
curr->device = makedev(devmajor, devminor);
curr->inode = (ino_t)inode;
curr->perms = 0U;
if (strchr(perms, 'r'))
curr->perms |= PERMS_READ;
if (strchr(perms, 'w'))
curr->perms |= PERMS_WRITE;
if (strchr(perms, 'x'))
curr->perms |= PERMS_EXEC;
if (strchr(perms, 's'))
curr->perms |= PERMS_SHARED;
if (strchr(perms, 'p'))
curr->perms |= PERMS_PRIVATE;
curr->next = list;
list = curr;
}
free(line);
if (!feof(maps) || ferror(maps)) {
fclose(maps);
free_mem_stats(list);
errno = EIO;
return NULL;
}
if (fclose(maps)) {
free_mem_stats(list);
errno = EIO;
return NULL;
}
errno = 0;
return list;
}
An example program to use the above, example.c:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "mem-stats.h"
int main(int argc, char *argv[])
{
int arg, pid;
char dummy;
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s PID\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "You can use PID 0 as an alias for the command itself.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
for (arg = 1; arg < argc; arg++)
if (sscanf(argv[arg], " %i %c", &pid, &dummy) == 1) {
address_range *list, *curr;
if (!pid)
pid = getpid();
list = mem_stats((pid_t)pid);
if (!list) {
fprintf(stderr, "Cannot obtain memory usage of process %d: %s.\n", pid, strerror(errno));
return EXIT_FAILURE;
}
printf("Process %d:\n", pid);
for (curr = list; curr != NULL; curr = curr->next)
printf("\t%p .. %p: %s\n", curr->start, (void *)((char *)curr->start + curr->length), curr->name);
printf("\n");
fflush(stdout);
free_mem_stats(list);
} else {
fprintf(stderr, "%s: Invalid PID.\n", argv[arg]);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
and a Makefile to make building it, simple:
CC := gcc
CFLAGS := -Wall -Wextra -O2 -fomit-frame-pointer
LDFLAGS :=
PROGS := example
.PHONY: all clean
all: clean $(PROGS)
clean:
rm -f *.o $(PROGS)
%.o: %.c
$(CC) $(CFLAGS) -c $^
example: mem-stats.o example.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $#
Note that the three indented lines in the Makefile above must use tab characters, not spaces. It seems that the editor here converts tabs to spaces, so you need to fix that, for example by using
sed -e 's|^ *|\t|' -i Makefile
If you don't fix the indentation, and use spaces in a Makefile, you'll see an error message similar to *** missing separator. Stop.
Some editors automatically convert a tab keypress into a number of spaces, so you may need to delve into the editor settings of whatever editor you use. Often, editors keep a pasted tab character intact, so you can always try pasting a tab from another program.
To compile and run, save the above files and run:
make
./example 0
to print the memory ranges used by the example program itself. If you want to see, say, the memory ranges used by your PulseAudio daemon, run:
./example $(ps -o pid= -C pulseaudio)
Note that standard access restrictions apply. A normal user can only see the memory ranges of the processes that run as that user; otherwise you need superuser privileges (sudo or similar).
Another alternative is pmap tool which dumps the process memory mapping details:
pmap [ -x | -d ] [ -q ] pids...
pmap -V
pmap is part of procps collection.
Also if you are interest in physical mapping, you can take a look at pagemap, which is made available in recent Linux Kernel to let process know it's physical memory info. It might be useful for user space driver development where user space process need to find physical address of a buffer as DMA destination.
https://www.kernel.org/doc/Documentation/vm/pagemap.txt
If you're on Linux use gcore to get a static core dump, it's part of gdb...
gcore $pid > Corefile
or
gcore -o core_dump $pid
To debug a running program attach to it using gdb
gdb -p 1234
then poke around in it. To see how it's layed out
(gdb) maint info sections
Exec file:
`/home/foo/program', file type elf32-i386.
[0] 0x8048134->0x8048147 at 0x00000134: .interp ALLOC LOAD READONLY DATA HAS_CONTENTS
[1] 0x8048148->0x8048168 at 0x00000148: .note.ABI-tag ALLOC LOAD READONLY DATA HAS_CONTENTS
[2] 0x8048168->0x804818c at 0x00000168: .note.gnu.build-id ALLOC LOAD
.....
.....
[23] 0x8049a40->0x8049ad1 at 0x00000a40: .data ALLOC LOAD DATA HAS_CONTENTS
[24] 0x8049ad1->0x8049ad4 at 0x00000ad1: .bss ALLOC
To poke around in registers use
(gdb) info all-registers
eax 0xfffffdfc -516
ecx 0x0 0
edx 0x1 1
ebx 0xffeedc28 -1123288
esp 0xffeedc0c 0xffeedc0c
ebp 0xffeedc78 0xffeedc78
esi 0x1308 4872
edi 0x45cf 17871
.... snipped
If you want to see the assembly used for a particular function use disassemble. It can also be used with addresses in memory.
(gdb) disassemble main
Dump of assembler code for function main:
0x080483f0 <+0>: lea 0x4(%esp),%ecx
0x080483f4 <+4>: and $0xfffffff0,%esp
0x080483f7 <+7>: mov $0x8048780,%edx
0x080483fc <+12>: pushl -0x4(%ecx)
0x080483ff <+15>: push %ebp
0x08048400 <+16>: mov %esp,%ebp
....
....

shmget: Operation not permitted

HugeTLB - Large Page Support in the Linux Kernel
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#define MB_1 (1024*1024)
#define MB_8 (8*MB_1)
char *a;
int shmid1;
void init_hugetlb_seg()
{
shmid1 = shmget(2, MB_8, SHM_HUGETLB
| IPC_CREAT | SHM_R
| SHM_W);
if ( shmid1 < 0 ) {
perror("shmget");
exit(1);
}
printf("HugeTLB shmid: 0x%x\n", shmid1);
a = shmat(shmid1, 0, 0);
if (a == (char *)-1) {
perror("Shared memory attach failure");
shmctl(shmid1, IPC_RMID, NULL);
exit(2);
}
}
void wr_to_array()
{
int i;
for( i=0 ; i<MB_8 ; i++) {
a[i] = 'A';
}
}
void rd_from_array()
{
int i, count = 0;
for( i=0 ; i<MB_8 ; i++)
if (a[i] == 'A') count++;
if (count==i)
printf("HugeTLB read success :-)\n");
else
printf("HugeTLB read failed :-(\n");
}
int main(int argc, char *argv[])
{
init_hugetlb_seg();
printf("HugeTLB memory segment initialized !\n");
printf("Press any key to write to memory area\n");
getchar();
wr_to_array();
printf("Press any key to rd from memory area\n");
getchar();
rd_from_array();
shmctl(shmid1, IPC_RMID, NULL);
return 0;
}
Question> I don't have root permission to run this code. What should I do to fix the permission issue?
$ gcc hugetlb-array.c -o hugetlb-array -Wall
$ ./hugetlb-array
shmget: Operation not permitted
Without using SHM_HUGETLB, the code runs well without problem.
$ ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000002 32768 myid 600 2097152 1
You need the CAP_IPC_LOCK capability. You can add this to an executable using setcap(8).
Specifically, run:
root#yourmachine$ setcap cap_ipc_lock=ep your_executable
This has to be redone every time your executable is modified (recompiled/reinstalled) - otherwise there would be a gaping security hole.
If you only need to do this at startup, you should also consider dropping privileges as soon as possible, but this is not essential (if anyone really cares, you'll probably get a patch).
See also Using setcap in linux

Segfault when reading .dynstr (strtab) in an ELF file

I'm trying to read the names of shared libraries used in binaries, using the C language. So far, I have the following program in test.c:
#include <string.h>
#include <sys/mman.h>
#include <elf.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char **argv) {
const char *ls = NULL;
int fd = -1;
struct stat stat = {0};
if (argc != 2) {
printf("Missing arg\n");
return 0;
}
// open the file in readonly mode
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
perror("open");
goto cleanup;
}
// get the file size
if (fstat(fd, &stat) != 0) {
perror("stat");
goto cleanup;
}
// put the file in memory
ls = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (ls == MAP_FAILED) {
perror("mmap");
goto cleanup;
}
Elf64_Ehdr *eh = (Elf64_Ehdr *)ls;
// looking for the PT_DYNAMIC segment
for (int i = 0; i < eh->e_phnum; i++) {
Elf64_Phdr *ph = (Elf64_Phdr *)((char *)ls + (eh->e_phoff + eh->e_phentsize * i));
const char *strtab = NULL;
if (ph->p_type == PT_DYNAMIC) {
const Elf64_Dyn *dtag_table = (const Elf64_Dyn *)(ls + ph->p_offset);
// looking for the string table
for (int j = 0; 1; j++) {
// the end of the dtag table is marked by DT_NULL
if (dtag_table[j].d_tag == DT_NULL) {
break;
}
if (dtag_table[j].d_tag == DT_STRTAB) {
strtab = (const char *)dtag_table[j].d_un.d_ptr;
printf("string table addr: %p\n", strtab);
}
}
// no string table ? we're stuck, bail out
if (strtab == NULL) {
printf("no strtab, abort\n");
break;
}
// now, i print shared libraries
for (int j = 0; 1; j++) {
// the end of the dtag table is marked by DT_NULL
if (dtag_table[j].d_tag == DT_NULL) {
break;
}
if (dtag_table[j].d_tag == DT_NEEDED) {
printf("too long: %d\n", &strtab[dtag_table[j].d_un.d_val] >= ls + stat.st_size);
printf("string offset in strtab: %lu\n", dtag_table[j].d_un.d_val);
printf("string from strtab: %s\n", &strtab[dtag_table[j].d_un.d_val]);
}
}
// only go through the PT_DYNAMIC segment we found,
// other segments dont matter
break;
}
}
// cleanup memory
cleanup:
if (fd != -1) {
close(fd);
}
if (ls != MAP_FAILED) {
munmap((void *)ls, stat.st_size);
}
return 0;
}
I compile it with:
gcc -g -Wall -Wextra test.c
When I run ./a.out a.out (so reading the shared libraries of itself), it seems to work fine, I get the following output:
string table addr: 0x4003d8
too long: 0
string offset in strtab: 1
string from strtab: libc.so.6
However, when I run it against system binaries, like /bin/ls, with ./a.out /bin/ls, then I get a segfault at line 78.
string table addr: 0x401030
too long: 0
string offset in strtab: 1
Segmentation fault (core dumped)
I don't know why. Reading the /bin/ls with readelf, the adresses I use seem right and the string offset seems right too:
$ readelf -a /bin/ls | grep dynstr
...
[ 6] .dynstr STRTAB 0000000000401030 00001030
...
$ readelf -p .dynstr /bin/ls
String dump of section '.dynstr':
[ 1] libselinux.so.1
...
What am I doing wrong?
What am I doing wrong?
For a non-PIE binary, this:
strtab = (const char *)dtag_table[j].d_un.d_ptr;
points to where the .dynstr would have been if that binary was actually running in current process.
If the binary is not running, you need to relocate this value by $where_mmaped - $load_addr. The $where_mmaped is your ls variable. The $load_addr is the address where the binary was statically linked to load (usually it's the p_vaddr of the first PT_LOAD segment; for x86_64 binary the typical value is 0x400000).
You'll note that this neatly explains why ./a.out a.out works: you read .dynstr from your own address space, while using a.out to figure out correct offsets.

How to get USB connected hard disk serial in linux?

I'm having a requirement to create a file in the externally mounted hard disk .created file should contain the serial no of the harddisk and that file can be used by other process.
I tried to use the following code
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/hdreg.h>
int main(int argc, char *argv[])
{
static struct hd_driveid hd;
int fd;
if (geteuid() > 0) {
printf("ERROR: Must be root to use\n");
exit(1);
}
if ((fd = open(argv[1], O_RDONLY|O_NONBLOCK)) < 0) {
printf("ERROR: Cannot open device %s\n", argv[1]);
exit(1);
}
if (!ioctl(fd, HDIO_GET_IDENTITY, &hd)) {
printf("Hard Disk Model: %.40s\n", hd.model);
printf(" Serial Number: %.20s\n", hd.serial_no);
} else if (errno == -ENOMSG) {
printf("No hard disk identification information available\n");
} else {
perror("ERROR: HDIO_GET_IDENTITY");
exit(1);
}
exit(0);
}
this is working fine for internal hard disk but when i do this for external hard disk(usb) it is giving me the following error
ERROR: HDIO_GET_IDENTITY: Invalid argument
Because the device is connected to a USB bridge, you can't send the HDIO_GET_IDENTITY command.
You can try hdparm to query the identity of the device. With the default options, hdparm fails to identify the device so you have to specify the type of the device with -d (see USB devices and smartmontools).
Without the -d option, I get:
$ sudo smartctl /dev/sdc
/dev/sdc: Unknown USB bridge [0x059f:0x1011 (0x000)]
Please specify device type with the -d option.
With -d sat,auto, hdparm manages to display some information about the device:
$ sudo smartctl -d sat,auto -i /dev/sdc
/dev/sdc [SCSI]: Device open changed type from 'sat,auto' to 'scsi'
=== START OF INFORMATION SECTION ===
Vendor: ST2000VN
Product: 000-1H3164
User Capacity: 2 000 398 934 016 bytes [2,00 TB]
Logical block size: 512 bytes
Device type: disk
Local Time is: Thu Mar 13 09:41:32 2014 CET
SMART support is: Unavailable - device lacks SMART capability.
You can try to do the same thing as smartctl in your C program, but it's probably easier to write a script that invokes smartctl.
Thanks for the explanation and i got the below to identify the serial no of a external hardisk
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <scsi/scsi.h>
#include <scsi/sg.h>
#include <sys/ioctl.h>
int scsi_get_serial(int fd, void *buf, size_t buf_len) {
// we shall retrieve page 0x80 as per http://en.wikipedia.org/wiki/SCSI_Inquiry_Command
unsigned char inq_cmd[] = {INQUIRY, 1, 0x80, 0, buf_len, 0};
unsigned char sense[32];
struct sg_io_hdr io_hdr;
int result;
memset(&io_hdr, 0, sizeof (io_hdr));
io_hdr.interface_id = 'S';
io_hdr.cmdp = inq_cmd;
io_hdr.cmd_len = sizeof (inq_cmd);
io_hdr.dxferp = buf;
io_hdr.dxfer_len = buf_len;
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.sbp = sense;
io_hdr.mx_sb_len = sizeof (sense);
io_hdr.timeout = 5000;
result = ioctl(fd, SG_IO, &io_hdr);
if (result < 0)
return result;
if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK)
return 1;
return 0;
}
void trim(char * s) {
char * p = s;
int l = strlen(p);
while(isspace(p[l - 1])) p[--l] = 0;
while(* p && isspace(* p)) ++p, --l;
memmove(s, p, l + 1);
}
int storeData (char *filepath, char *data) {
int rc = 0;
FILE *fOut = fopen (filepath, "a");
if (fOut != NULL) {
if (fputs (data, fOut) != EOF) {
rc = 1;
}
fclose (fOut); // or for the paranoid: if (fclose (fOut) == EOF) rc = 0;
}
return rc;
}
int main(int argc, char** argv) {
if(argc>1){
char *dev = (char *)argv[1];
char outStr[1024];
printf("\nEntered Serial no : %s\n",argv[1]);
char scsi_serial[255];
int rc;
int fd;
fd = open(dev, O_RDONLY | O_NONBLOCK);
if (fd < 0) {
perror(dev);
}
memset(scsi_serial, 0, sizeof (scsi_serial));
rc = scsi_get_serial(fd, scsi_serial, 255);
// scsi_serial[3] is the length of the serial number
// scsi_serial[4] is serial number (raw, NOT null terminated)
if (rc < 0) {
printf("FAIL, rc=%d, errno=%d\n", rc, errno);
} else
if (rc == 1) {
printf("FAIL, rc=%d, drive doesn't report serial number\n", rc);
} else {
if (!scsi_serial[3]) {
printf("Failed to retrieve serial for %s\n", dev);
return -1;
}
printf("Serial Number: %.*s\n", (size_t) scsi_serial[3], (char *) & scsi_serial[4]);
scsi_serial[4+scsi_serial[3]]='\0';
trim(&scsi_serial[4]);
sprintf(outStr,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?> \n<!DOCTYPE properties SYSTEM \"http://java.sun.com/dtd/properties.dtd\"> \n<properties>\n<comment/>\n<entry key=\"SerialNo\">%s</entry>\n</properties>\n", (char *) & scsi_serial[4]);
//strcat((char *)argv[2],(char *)"/hdd.xml");
printf("\n%s",outStr);
// printf("\n%s",(char *)argv[2]);
//storeData((char *)argv[1],(char *) outStr);
}
close(fd);
}else{
printf("\nInsufficient no of arguments \n");
}
return (EXIT_SUCCESS);
}

Resources