in reply to using syscalls in perl through inline c

You didn't really ask a question, so I assumed that you were wondering how to fix the C code to get it to do what you want. It looks like you copy/pasted code from different sources or examples online (by the looks of it). In your listfiles() function, you're using argc, argv which is wrong (those are command line arguments, which belong to a main() function in a program. They don't have any use inside of a non-entry-point library, which is essentially what you've got here). I changed the parameter list to a single const char * dir, then changed the argv[1] to dir. I've inserted comments in the C code of what was changed:

#!/usr/bin/env perl use 5.012; use strict; use warnings; my $str = listfiles('/home/steve/test'); print $str; use Inline C => <<'END_OF_C_CODE'; #include <dirent.h> /* Defines DT_* constants */ #include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/syscall.h> #define handle_error(msg) do { perror(msg); exit(EXIT_FAILURE); } whil +e (0) struct linux_dirent { long d_ino; off_t d_off; unsigned short d_reclen; char d_name[]; }; #define BUF_SIZE 1024*1024*5 /**** CHANGED LINE BELOW ***/ int listfiles(const char * dir) { int fd, nread; char buf[BUF_SIZE]; struct linux_dirent *d; int bpos; char d_type; /**** CHANGED LINE BELOW ****/ fd = open(dir, O_RDONLY | O_DIRECTORY); if (fd == -1) handle_error("open"); for ( ; ; ) { nread = syscall(SYS_getdents, fd, buf, BUF_SIZE); if (nread == -1) handle_error("getdents"); if (nread == 0) break; for (bpos = 0; bpos < nread;) { d = (struct linux_dirent *) (buf + bpos); if (d->d_ino != 0) printf("%s\n", (char *) d->d_name); bpos += d->d_reclen; } } exit(EXIT_SUCCESS); } END_OF_C_CODE

Output:

one.txt two.txt

Now, if you want to be able to send in a directory as an argument, do it in Perl:

if (! defined $ARGV[0]){ print "Usage: script.pl <directory>\n"; exit; } my $dir = $ARGV[0]; my $str = listfiles($dir); ...

You may want to do extra argument checking in Perl, or you can just let the back end report any issues with bad dir names.

Replies are listed 'Best First'.
Re^2: using syscalls in perl through inline c
by ofer (Novice) on Jan 16, 2017 at 15:46 UTC
    actually what I was struggling with is how to get the data from the syscall as a scalar I played so much with the original code I just pasted it from the source without my changes by mistake... Thanks
      This is what I ended up with the C function returns a reference to a hash with all files listed I might add a second for loop so it'll list the file recursively, any suggestions welcome.
      #!/usr/bin/env perl use 5.012; use strict; use warnings; use Data::Dumper qw(Dumper); my $str = &listfiles('<directory>'); print Dumper \$str; use Inline C => <<'END_OF_C_CODE'; #include <dirent.h> /* Defines DT_* constants */ #include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/syscall.h> #define handle_error(msg) do { perror(msg); exit(EXIT_FAILURE); } whil +e (0) struct linux_dirent { long d_ino; off_t d_off; unsigned short d_reclen; char d_name[]; }; #define BUF_SIZE 1024*1024*5 SV* listfiles(const char * dir) { int fd, nread; char buf[BUF_SIZE]; struct linux_dirent *d; int bpos; char d_type; char inode; HV* hash = newHV(); fd = open(dir, O_RDONLY | O_DIRECTORY); if (fd == -1) handle_error("open"); for ( ; ; ) { nread = syscall(SYS_getdents, fd, buf, BUF_SIZE); if (nread == -1) handle_error("getdents"); if (nread == 0) break; for (bpos = 0; bpos < nread;) { d = (struct linux_dirent *) (buf + bpos); if (d->d_ino != 0) { size_t nbytes = snprintf(NULL, 0, "%d", d->d_ino) + 1; char *inode = malloc(nbytes); snprintf(inode, nbytes, "%d", d->d_ino); hv_store(hash, inode, strlen(inode), newSVpvf("%s",(cha +r *) d->d_name, 0), 0); } bpos += d->d_reclen; } } return newRV_noinc((SV*) hash); } END_OF_C_CODE
        I added a second loop to list the files recursively, next in line is C multi threads and another sub hash to divide the record types easily.
        #!/usr/bin/env perl use 5.012; use strict; use warnings; use Data::Dumper qw(Dumper); my $str = &listfiles('<directory>'); print Dumper \$str; use Inline C => <<'END_OF_C_CODE'; #include <dirent.h> /* Defines DT_* constants */ #include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/syscall.h> #define handle_error(msg) do { perror(msg); exit(EXIT_FAILURE); } whil +e (0) struct linux_dirent { long d_ino; off_t d_off; unsigned short d_reclen; char d_name[]; }; #define BUF_SIZE 1024*1024*5 SV* listfiles(const char * dir) { int fd, nread; struct linux_dirent *d; int bpos; char inode, d_type; char * buf = malloc(BUF_SIZE); static HV* hash; if (!(hash)) hash = newHV(); fd = open(dir, O_RDONLY | O_DIRECTORY); if (fd == -1) handle_error("open"); for ( ; ; ) { nread = syscall(SYS_getdents, fd, buf, BUF_SIZE); if (nread == -1) handle_error("getdents"); if (nread == 0) break; for (bpos = 0; bpos < nread;) { d = (struct linux_dirent *) (buf + bpos); d_type = *(buf + bpos + d->d_reclen - 1); bpos += d->d_reclen; if(d->d_ino && strcmp(d->d_name, ".") && strcmp(d->d_name, + "..") && strcmp(d->d_name, ".snapshot")) { size_t nbytes = snprintf(NULL, 0, "%d", d->d_ino) + 1; char *inode = malloc(nbytes); snprintf(inode, nbytes, "%d", d->d_ino); hv_store(hash, inode, strlen(inode), newSVpvf("%s/%s",( +char *) dir,d->d_name, 0), 0); if(d_type == DT_DIR) { int dir_len = strlen(dir); char * subdir = calloc(1, PATH_MAX + 1); strcat(subdir, dir); strcat(subdir + dir_len, "/"); strcat(subdir + dir_len + 1, d->d_name); listfiles(subdir); free(subdir); } } } } close(fd); free(buf); return newRV_noinc((SV*) hash); } END_OF_C_CODE