How to make SQLite preallocate memory during startup? - c

I'm working on an embedded program that uses SQLite, and I want more predictable memory usage.
I read https://www.sqlite.org/malloc.html and wanted to use the "Zero-malloc memory allocator" described in section 3.1.4. If I understand it correctly, it tells SQLite to allocate a fixed amount of memory at startup, and use it exclusively, instead of making dynamic allocations (malloc) at runtime.
On my Ubuntu desktop (so nothing to do with embedded), I compiled the latest SQLite with -DSQLITE_ENABLE_MEMSYS5, created an example program which tells SQLite to preallocate 1GB RAM and then sleep for 15 seconds. I wrote a test script to check 'free' before and after running the example.
Unfortunately, I don't see the preallocation reflected in the 'free' output. What gives? My example checks every SQLite return code and I didn't get any errors. How can I make SQLite reserve the memory immediately during startup?
My test script:
#!/bin/bash
set -o verbose
free -b
./testsqlite3 &
sleep 3
ps aux | grep testsqlite3
free -b
Output:
$ ./runtest.sh
free -b
total used free shared buff/cache available
Mem: 8421400576 607322112 6244298752 28102656 1569779712 7066603520
Swap: 0 0 0
./testsqlite3 &
sleep 3
Using libsqlite version 3.40.0, sqlite3.h is version 3.40.0
Preallocating using MEMSYS5
Opened database successfully
Table created successfully
Sleeping for 15 secs before exiting. Call 'free -b' now.
echo "bash sleep complete, confirming app still running"
bash sleep complete, confirming app still running
ps aux | grep testsqlite3
user1 7474 0.0 0.0 979960 2380 pts/1 S+ 12:11 0:00 ./testsqlite3
user1 7477 0.0 0.0 5116 880 pts/1 S+ 12:11 0:00 grep testsqlite3
free -b
total used free shared buff/cache available
Mem: 8421400576 607420416 6244200448 28102656 1569779712 7066505216
Swap: 0 0 0
My example program testsqlite3:
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <unistd.h>
static int callback(void *NotUsed, int argc, char **argv, char **azColName) {
int i;
for(i = 0; i<argc; i++) {
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
int main(int argc, char* argv[]) {
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char *sql;
fprintf(stdout, "Using libsqlite version %s, sqlite3.h is version %s\n", sqlite3_libversion(), SQLITE_VERSION);
fprintf(stdout, "Preallocating using MEMSYS5\n");
//int preallocateMemoryBytes = 100000000; //100MB
int preallocateMemoryBytes = 1000000000; //1GB
rc = sqlite3_config(SQLITE_CONFIG_HEAP,
malloc(preallocateMemoryBytes),
preallocateMemoryBytes, 32);
if (rc != SQLITE_OK) {
fprintf(stderr, "failed to config SQLITE_CONFIG_HEAP\n");
return(-1);
}
rc = sqlite3_initialize();
if (rc != SQLITE_OK) {
fprintf(stderr, "initialize() failed\n");
return(-1);
}
//open DB
rc = sqlite3_open("test.db", &db);
if( rc ) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return(-1);
} else {
fprintf(stdout, "Opened database successfully\n");
}
sql = "CREATE TABLE IF NOT EXISTS COMPANY(" \
"ID INT PRIMARY KEY NOT NULL," \
"NAME TEXT NOT NULL," \
"AGE INT NOT NULL," \
"ADDRESS CHAR(50)," \
"SALARY REAL );";
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
if( rc != SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
return(-1);
} else {
fprintf(stdout, "Table created successfully\n");
}
fprintf(stdout, "Sleeping for 15 secs before exiting. Call 'free -b' now.\n");
usleep(15000 * 1000);
sqlite3_close(db);
return 0;
}

Related

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

Get hardware information from /proc filesytem in Linux

I use execv to run lshw command to get the CPU, disk, and memory in C code. But I would like to search another solution to get these information from /proc or any other existed data. Have any suggestion? Here is my code:
char *params[9] = {"/usr/bin/lshw", "-short", "-c", "disk",
"-c", "memory", "-c", "processor", 0}; //cmd params filled
execv(params[0], params);
Linux command: $ sudo lshw -short -c disk -c processor -c memory
$ sudo lshw -short -c disk -c processor -c memory
H/W path Device Class Description
======================================================
/0/0 memory 64KiB BIOS
/0/22 memory 16GiB System Memory
/0/22/0 memory DIMM Synchronous [empty]
/0/22/1 memory DIMM Synchronous [empty]
/0/22/2 memory 8GiB DIMM Synchronous 2133 MHz (0.5 ns)
/0/22/3 memory 8GiB DIMM Synchronous 2133 MHz (0.5 ns)
/0/2a memory 256KiB L1 cache
/0/2b memory 1MiB L2 cache
/0/2c memory 6MiB L3 cache
/0/2d processor Intel(R) Xeon(R) CPU D-1521 # 2.40GHz
/0/1/0.0.0 /dev/sda disk 16GB SATADOM-SH 3IE3
/0/2/0.0.0 /dev/sdb disk 120GB Patriot Blaze
I have two questions:
Where to find a guide to parse the files in /proc to get
these hardware information?
Do I need to trace the source code of lshw to find what does lshw do?
Edit:
Chapter 7 of Advanced Linux Programming is a guide to parse the /proc filesystem.
The best way to get hardware information is using sysconf() and sysctl*() functions (Mac OS X, freebsd, openbsd), and sysconf() and sysinfo() on Linux.
Parsing /proc/* is slower and more involved than calling sysinfo( ) or sysconf( )
Below is a small example giving you some information about processor and memory on Mac OS X:
#include <sys/types.h>
#include <sys/sysctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main()
{
char *p = NULL;
size_t len;
sysctlbyname("hw.model", NULL, &len, NULL, 0);
p = malloc(len);
sysctlbyname("hw.model", p, &len, NULL, 0);
printf("%s\n", p);
/* CTL_MACHDEP variables are architecture dependent so doesn't work
for every one */
sysctlbyname("machdep.cpu.brand_string", NULL, &len, NULL, 0);
p = malloc(len);
sysctlbyname("machdep.cpu.brand_string", p, &len, NULL, 0);
printf("%s\n", p);
int64_t mem;
len = sizeof(mem);
sysctlbyname("hw.memsize", &mem, &len, NULL, 0);
printf("System Memory : %lld\n", mem);
return (0);
}
You have to read man 3 sysctl, or on Linux man 2 sysconf and man 2 sysinfo.
An interesting link : http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system#Other
You can calculate the CPU load and usage retrieving some sysctl variables, and doing the math by yourself (you can find the formulas to do it on google).
But where to find the physical DIMM information as the report from $ sudo lshw -short -c memory ?
You can execute your command inside your C program to save it as a string like :
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
char *strjoin(char *s1, char *s2, int n)
{
int i = strlen(s2);
int j = 0;
if ((s2 = realloc(s2, (i + n + 1))) == NULL)
perror(0);
while (j < n && s1[j])
{
s2[i] = s1[j];
i++;
j++;
}
s2[i] = 0;
return (s2);
}
int main()
{
pid_t father;
char buf[500] = {0};
char *str;
char *argv[5] = {"/usr/bin/lshw", "-short", "-c", "memory"};
int fd[2];
int ret;
if (pipe(fd) == -1)
{
perror(NULL);
return -1;
}
father = fork();
if (father == 0)
{
close(fd[1]);
while ((ret = read(fd[0], buf, 500)))
{
str = strjoin(buf, str, ret);
}
close(fd[0]);
}
else
{
close(fd[0]);
execv(argv[0], argv);
close(fd[1]);
wait(0);
}
wait(0);
printf("%s", str);
return 0;
}
(I don't check all the function's return in this code, not to have a too long one, but you should do it in your program).
Here is an example of parsing the file /proc/meminfo to save in a double array 2 strings I want, and then printing them out :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
FILE *f;
char *line = NULL;
ssize_t read;
size_t len = 0;
char **info;
int i = 0;
info = malloc(3 * sizeof(char*));
f = fopen("/proc/meminfo", "r");
while ((read = getline(&line, &len, f)) != -1)
{
if (strstr(line, "MemTotal") != NULL)
info[i] = strdup(line);
else if (strstr(line, "MemFree") != NULL)
info[i] = strdup(line);
i++;
}
info[i] = 0;
fclose(f);
i = 0;
while (info[i])
{
printf("%s", info[i]);
free (info[i]);
i++;
}
free (info);
return 0;
}
If you want to save more strings, malloc more space in the double array info, and add them with else if inside the read loop. You can do that with any files from /proc/ to get the information you need.
By reading the source code of lshw, I found that lshw read raw data from /sys/class/dmi/. Because lshw is written in CPP that I am not familiar with, there is a question Where does dmidecode get the SMBIOS table? mentioned that dmidecode.c read raw data from /sys/class/dmi that's same as lshw does.
Here are the definitions in dmidecode.c
#define SYS_ENTRY_FILE "/sys/firmware/dmi/tables/smbios_entry_point"
#define SYS_TABLE_FILE "/sys/firmware/dmi/tables/DMI"
I extract code from dmidecode.c to get the CPU and memory information, and use lsscsi to get disk information.
Thanks for your help.
1: To get the CPU load use this command :
top -bn1 | grep load
this will give you output like :
top - 12:26:20 up 35 min, 2 users, load average: 0.02, 0.01, 0.00
now parse the load average from above string.
2: To get memory info use this command :
free -m
This will give you:
total used free shared buffers cached
Mem: 15926 308 15617 6 15 122
-/+ buffers/cache: 171 15755
Swap: 0 0 0
To get the disk info , use this :
df -H /home/test
This will give you :
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 102G 5.4G 91G 6% /
Now from above result parse the content what you want.

Bad 'ls' Command Behavior In Custom Simple Shell

I am having an issue that is just seeming to slip past my knowledge. I am writing a simple shell to learn some systems programming for an internship coming up with Unisys. In my shell, it seems that all of the commands I am trying are working besides the ls and even now discovering the wc command. ls and wc works when I type it by itself, but if I give it arguments, it will fail to work and give me an error saying No such file or directory.
here is my code:
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#define BUF_SIZE 1024
#define DELIMS " -\r\t\n"
/****************************************************************
* Capture input from the user. Returns the input from the
* standard input file descriptor.
***************************************************************/
char * getInput (char **buffer, size_t buflen)
{
size_t bufsize = BUF_SIZE;
*buffer = malloc(sizeof(char) * bufsize + 1); // allocate space for the buffer
if (!*buffer)
{
fprintf(stderr, "Shell: buffer allocation error\n");
exit(EXIT_FAILURE);
}
printf("$$ ");
fflush(NULL);
int bytesRead = getline(&(*buffer), &bufsize, stdin);
if (bytesRead < 0)
{
printf("Getline error\n");
exit(EXIT_FAILURE);
}
return *buffer; // Not capturing return value right now
}
/****************************************************************
* Tokenize the buffer input from stdin
***************************************************************/
char ** splitLine(char *line)
{
int bufsize = BUF_SIZE;
int pos = 0;
char **tokens = malloc (sizeof(char) * BUF_SIZE + 1);
char *token;
if (!tokens)
{
fprintf(stderr, "Shell: buffer allocation error\n");
exit(EXIT_FAILURE);
}
/* Tokenize the line */
token = strtok(line, DELIMS);
while (token != NULL)
{
tokens[pos] = token;
pos++;
if (pos > bufsize)
{
bufsize += BUF_SIZE;
tokens = realloc(tokens, bufsize * sizeof(char) + 1);
if (!tokens)
{
fprintf(stderr, "Shell: buffer allocation error\n");
exit(EXIT_FAILURE);
}
}
token = strtok(NULL, DELIMS); // continue grabbing tokens
}
tokens[pos] = NULL;
return tokens;
}
/****************************************************************
* Main function
***************************************************************/
int main (int argc, char **argv)
{
char *buf; // buffer to hold user input from standard input stream.
pid_t pid; // Parent id of the current process
int status;
/* Loop while the user is getting input */
while (getInput(&buf, sizeof(buf)))
{
char **args = splitLine(buf);
int i = 0;
/* Print tokens just to check if we are processing them correctly */
while (1)
{
char *token = args[i++];
if (token != NULL)
printf("Token #%d: %s\n", i, token);
else
break;
}
fflush(NULL);
/* Fork and execute command in the shell */
pid = fork();
switch(pid)
{
case -1:
{
/* Failed to fork */
fprintf(stderr, "Shell cannot fork: %s\n", strerror(errno));
continue;
}
case 0:
{
/* Child so run the command */
execvp(args[0], args); // Should not ever return otherwise there was an error
fprintf(stderr, "Shell: couldn't execute %s: %s\n ", buf, strerror(errno));
exit(EX_DATAERR);
}
}
/* Suspend execution of calling process until receiving a status message from the child process
or a signal is received. On return of waitpid, status contains the termination
information about the process that exited. The pid parameter specifies the set of child
process for which to wait for */
if ((pid = waitpid(pid, &status, 0) < 0))
{
fprintf(stderr, "Shell: waitpid error: %s\n", strerror(errno));
}
free(args);
}
free(buf);
exit(EX_OK);
}
For example, I have tried the following commands with output:
ls -la (THE ISSUE)
$$ ls -la
Token #1: ls
Token #2: la
ls: la: No such file or directory
$$
wc -l (THE ISSUE)
$$ wc -l
Token #1: wc
Token #2: l
wc: l: open: No such file or directory
ls
$$ ls
Token #1: ls
Makefile driver driver.dSYM main.c main.o
$$
ps -la
$$ ps -la
Token #1: ps
Token #2: la
UID PID PPID CPU PRI NI VSZ RSS WCHAN STAT TT TIME COMMAND
0 2843 2405 0 31 0 2471528 8 - Us s000 0:00.08 login
501 2845 2843 0 31 0 2463080 1268 - S s000 0:01.08 -bash
501 4549 2845 0 31 0 2454268 716 - S+ s000 0:00.01 ./driv
0 4570 4549 0 31 0 2435020 932 - R+ s000 0:00.00 ps la
$$
which which
$$ which which
Token #1: which
Token #2: which
/usr/bin/which
which -a which
$$ which -a which
Token #1: which
Token #2: a
Token #3: which
/usr/bin/which
and even finally man getline
GETLINE(3) BSD Library Functions Manual GETLINE(3)
NAME
getdelim, getline -- get a line from a stream
LIBRARY
Standard C Library (libc, -lc)
.
.
.
Can anybody help me point out why I am having this issue?
Youve added "-" as a word seperator in the DELIMS macro.
Removing it should fix your problem.
As an aside, its probably best to avoid macros where you can do so easily. Here, I would have used a const char* delims to store the separators. I usually find it easier to declare a variable close to where its used - I think that makes it easier to spot bugs and read the code.

Making a shared data structure in 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).

Checking username: getpwnam / getpwnam_r: No such file or directory

I'm trying to make a web logging and I use getpwnam() function to check username existing. But for valid username getpwnam returns error: No such file or directory. So I tried getpwnam_r(), but it also failed with the same error. I'm running on embedded arm linux and I use /etc/passwd for password storing (I don't have /etc/shadow). My test program is:
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int
main(int argc, char *argv[])
{
struct passwd pwd;
struct passwd *result;
char *buf;
size_t bufsize;
int s;
if (argc != 2) {
fprintf(stderr, "Usage: %s username\n", argv[0]);
exit(EXIT_FAILURE);
}
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
if (bufsize == -1) /* Value was indeterminate */
bufsize = 16384; /* Should be more than enough */
buf = malloc(bufsize);
if (buf == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
s = getpwnam_r(argv[1], &pwd, buf, bufsize, &result);
if (result == NULL) {
if (s == 0)
printf("Not found\n");
else {
errno = s;
perror("getpwnam_r");
}
exit(EXIT_FAILURE);
}
printf("Name: %s; UID: %ld\n", pwd.pw_gecos, (long) pwd.pw_uid);
exit(EXIT_SUCCESS);
}
Password file can be written only by root:
/ # ls -l /etc/passwd
-rw-r--r-- 1 root root 207 Jan 1 00:29 /etc/passwd
/ #
I also tried to run my program (test) with root rights, but it also failed when I gave it an existing username.
/ # /tmp/test admin
getpwnam_r: No such file or directory
/ #
1) So, what I forgot about, or what should do additionally?
2) Do I need to use /etc/shadow file for storing passwords for system users?
Update:
My passwd file is:
~ # cat /etc/passwd
root:b6MVch7fPLasN:0:0:root:/home/root:/bin/ash
admin:8Mt/Jtxcyg8AY:1000:1000:admin:/tmp:/tmp/cli
user:5v4HoPrA9NtUo:1001:1000:user:/tmp:/tmp/cli
~ #
Thanks in advance! Bakir
1) The search service or method used in the password database (/etc/passwd) is defined in /etc/nsswitch.conf. To use this service getpwnam function calls shared library in the lib directory: /lib/libnss_SERVICE.so.x, where SERVICE is the search method. In my case compat is default method because of absent of /etc/nsswitch.conf. So, I was need to add libnss_compat.so.2 to /lib.
strace is useful thing!
Many thanks to osqx and alk!

Resources