ripmime permissions for attachments - file-permissions

We're using ripmime with Procmail to extract email contents into files. When extracting the email body (the text) ripmime correctly uses the configured procmail UMASK (022) for the files, but when there is an attachment it creates the file for the attachement with a 077 umask. Here's an example of files that ripmime created for one email that had a "testTrades2.csv" attachment:
-rw-r--r-- 1 fsdevprod fsdevprod 2341 2012-06-07 06:36 textfile4
-rw-r--r-- 1 fsdevprod fsdevprod 19 2012-06-07 06:36 textfile3
-rw-r--r-- 1 fsdevprod fsdevprod 294 2012-06-07 06:36 textfile2
-rw-r--r-- 1 fsdevprod fsdevprod 573 2012-06-07 06:36 textfile1
-rw-r--r-- 1 fsdevprod fsdevprod 0 2012-06-07 06:36 textfile0
-rw------- 1 fsdevprod fsdevprod 66 2012-06-07 06:36 testTrades2.csv
Here's how ripmime is being called in the procmail rc file:
| ripmime -i - -d /tmp
Why does the "testTrades2.csv" have different permissions from the textfile* files, and is there any way to have it use the same UMASK?
We're on ripmime v1.4.0.9.
thanks,
David

The ripmime source (mime.c) had a bunch of these:
open(fullpath, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
So it was hardcoded. I changed them to be this:
open(fullpath, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
and recompiled. Now the files are created group and publicly readable. Not an ideal solution as it is also hard-coded, but it works for me.
Ideally it should be command line configurable which shouldn't be hard to do, and then sent to the ripmime maintainer.

:0:
* ^From.*xxx#xxx.ru
{
:0 c:
| ripmime -i - --no-nameless -d $MAILDIR/xxx
:0:
| chmod 777 $MAILDIR/xxx/*
}

Related

Find a specific path with "find" dynamically

I am trying to find a specific file/folder based on directory name . There are one thing i could not understand which is tabcmd -> ../bin.2012....
When i did a ls -lrt on a specific path. for e.g
cmd : ls -lrt /opt/tableau/tableau_server/packages/customer-bin.20212.21.1217.2251
it will list as
lrwxrwxrwx. 1 root root 33 Dec 23 07:56 atrdiag -> ../bin.20212.21.1217.2251/atrdiag
lrwxrwxrwx. 1 root root 32 Dec 23 07:56 tabcmd -> ../bin.20212.21.1217.2251/tabcmd
lrwxrwxrwx. 1 root root 39 Dec 23 07:56 serveractutil -> ../bin.20212.21.1217.2251/serveractutil
lrwxrwxrwx. 1 root root 34 Dec 23 07:56 odbcinst -> ../bin.20212.21.1217.2251/odbcinst
lrwxrwxrwx. 1 root root 31 Dec 23 07:56 iusql -> ../bin.20212.21.1217.2251/iusql
lrwxrwxrwx. 1 root root 30 Dec 23 07:56 isql -> ../bin.20212.21.1217.2251/isql
lrwxrwxrwx. 1 root root 37 Dec 23 07:56 custactutil -> ../bin.20212.21.1217.2251/custactutil
lrwxrwxrwx. 1 root root 29 Dec 23 07:56 tsm -> ../bin.20212.21.1217.2251/tsm
The objective is to find tabcmd path dynamically as this path will change from time to time. "I could not use whereis command as it is not registered as a system path/variable"
Alternatively, what i did was
TEST=`find /opt/tableau -type d -name "tabcmd"`
echo "Tabcmd path is : $TEST"
However with the cmd i tried ,, it is returning empty
it returned "Tabcmd path is : "
Update :
when i tried using command below
TABCMD_PATH=`find /opt/tableau/tableau_server -name "tabcmd"`
echo $TABCMD_PATH
/opt/tableau/tableau_server/packages/bin.20212.21.1217.2251/tabcmd /opt/tableau/tableau_server/packages/customer-bin.20212.21.1217.2251/tabcmd
this it will print 2 times instead of 1 and if this is the approach i should take ?
It is empty because you are finding for a directory called tabcmd, you have to find by file -type f
TEST=`find /opt/tableau -type d -name "tabcmd"`
echo "Tabcmd path is : $TEST"
And the following print two path because the file is in both paths, the file link and the symbolic link:
TABCMD_PATH=`find /opt/tableau/tableau_server -name "tabcmd"`
echo $TABCMD_PATH
/opt/tableau/tableau_server/packages/bin.20212.21.1217.2251/tabcmd /opt/tableau/tableau_server/packages/customer-bin.20212.21.1217.2251/tabcmd
You have to find the file excluding the symbolic link:
TABCMD_PATH=`find /opt/tableau/tableau_server -type f -name "tabcmd"`
echo $TABCMD_PATH

Copy and move's command effect on inode

I interpret inode as a pointer to the actual place where the file is stored.
But I have problem understanding:
If I use cp file1 file2 in a place where file2 already exists, the inode doesn't change. And If there is originally a hard-link to file2, they now both point to the new file just copied here.
The only reason I can think of is that Linux interprets this as modifying
the file instead of deleting and creating a new file. I don't understand why it's designed this way?
But when I use mv file1 file2, the inode changes to the inode of file1.
You are correct in stating that cp will modify the file instead of deleting and recreating.
Here is a view of the underlying system calls as seen by strace (part of the output of strace cp file1 file2):
open("file2", O_WRONLY|O_TRUNC) = 4
stat("file2", {st_mode=S_IFREG|0664, st_size=6, ...}) = 0
stat("file1", {st_mode=S_IFREG|0664, st_size=3, ...}) = 0
stat("file2", {st_mode=S_IFREG|0664, st_size=6, ...}) = 0
open("file1", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0664, st_size=3, ...}) = 0
open("file2", O_WRONLY|O_TRUNC) = 4
fstat(4, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
read(3, "hi\n", 65536) = 3
write(4, "hi\n", 3) = 3
read(3, "", 65536) = 0
close(4) = 0
close(3) = 0
As you can see, it detects that file2 is present (stat returns 0), but then opens it for writing (O_WRONLY|O_TRUNC) without first doing an unlink.
See for example POSIX.1-2017, which specifies that the destination file shall only be unlink-ed where it could not be opened for writing and -f is used:
A file descriptor for dest_file shall be obtained by performing
actions equivalent to the open() function defined in the System
Interfaces volume of POSIX.1-2017 called using dest_file as the path
argument, and the bitwise-inclusive OR of O_WRONLY and O_TRUNC as the
oflag argument.
If the attempt to obtain a file descriptor fails and the -f option is
in effect, cp shall attempt to remove the file by performing actions
equivalent to the unlink() function defined in the System Interfaces
volume of POSIX.1-2017 called using dest_file as the path argument. If
this attempt succeeds, cp shall continue with step 3b.
This implies that if the destination file exists, the copy will succeed (without resorting to -f behaviour) if the cp process has write permission on it (not necessarily run as the user that owns the file), even if it does not have write permission on the containing directory. By contrast, unlinking and recreating would require write permission on the directory. I would speculate that this is behind the reason why the standard is as it is.
The --remove-destination option on GNU cp will make it do instead what you thought ought to be the default.
Here is the relevant part of the output of strace cp --remove-destination file1 file2. Note the unlink this time.
stat("file2", {st_mode=S_IFREG|0664, st_size=6, ...}) = 0
stat("file1", {st_mode=S_IFREG|0664, st_size=3, ...}) = 0
lstat("file2", {st_mode=S_IFREG|0664, st_size=6, ...}) = 0
unlink("file2") = 0
open("file1", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0664, st_size=3, ...}) = 0
open("file2", O_WRONLY|O_CREAT|O_EXCL, 0664) = 4
fstat(4, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
read(3, "hi\n", 65536) = 3
write(4, "hi\n", 3) = 3
read(3, "", 65536) = 0
close(4) = 0
close(3) = 0
When you use mv and the source and destination paths are on the same file filesystem, it will do an rename, and this will have the effect of unlinking any existing file at the target path. Here is the relevant part of the output of strace mv file1 file2.
access("file2", W_OK) = 0
rename("file1", "file2") = 0
In either case where an destination path is unlinked (whether explicitly by unlink() as called from cp --remove-destination, or as part of the effect of rename() as called from mv), the link count of the inode to which it was pointing will be decremented, but it will remain on the filesystem if either the link count is still >0 or if any processes have open filehandles on it. Any other (hard) links to this inode (i.e. other directory entries for it) will remain.
Investigating using ls -i
ls -i will show the inode numbers (as the first column when combined with -l), which helps demonstrate what is happening.
Example with default cp action
$ rm file1 file2 file3
$ echo hi > file1
$ echo world > file2
$ ln file2 file3
$ ls -li file*
49 -rw-rw-r-- 1 myuser mygroup 3 Jun 13 10:43 file1
50 -rw-rw-r-- 2 myuser mygroup 6 Jun 13 10:43 file2
50 -rw-rw-r-- 2 myuser mygroup 6 Jun 13 10:43 file3
$ cp file1 file2
$ ls -li file*
49 -rw-rw-r-- 1 myuser mygroup 3 Jun 13 10:43 file1
50 -rw-rw-r-- 2 myuser mygroup 3 Jun 13 10:43 file2 <=== exsting inode
50 -rw-rw-r-- 2 myuser mygroup 3 Jun 13 10:43 file3 <=== exsting inode
(Note existing inode 50 now has size 3).
Example with --remove-destination
$ rm file1 file2 file3
$ echo hi > file1
$ echo world > file2
$ ln file2 file3
$ ls -li file*
49 -rw-rw-r-- 1 myuser mygroup 3 Jun 13 10:46 file1
50 -rw-rw-r-- 2 myuser mygroup 6 Jun 13 10:46 file2
50 -rw-rw-r-- 2 myuser mygroup 6 Jun 13 10:46 file3
$ cp --remove-destination file1 file2
$ ls -li file*
49 -rw-rw-r-- 1 myuser mygroup 3 Jun 13 10:46 file1
55 -rw-rw-r-- 1 myuser mygroup 3 Jun 13 10:47 file2 <=== new inode
50 -rw-rw-r-- 1 myuser mygroup 6 Jun 13 10:46 file3 <=== existing inode
(Note new inode 55 has size 3. Unmodified inode 50 still has size 6.)
Example with mv
$ rm file1 file2 file3
$ echo hi > file1
$ echo world > file2
$ ln file2 file3
$ ls -li file*
49 -rw-rw-r-- 1 myuser mygroup 3 Jun 13 11:05 file1
50 -rw-rw-r-- 2 myuser mygroup 6 Jun 13 11:05 file2
50 -rw-rw-r-- 2 myuser mygroup 6 Jun 13 11:05 file3
$ mv file1 file2
$ ls -li file*
49 -rw-rw-r-- 1 myuser mygroup 3 Jun 13 11:05 file2 <== existing inode
50 -rw-rw-r-- 1 myuser mygroup 6 Jun 13 11:05 file3 <== existing inode
#alaniwi's answer covers what is is happening, but there's an implicit why here as well.
The reason cp works the way it does is to provide a way of replacing a file with mulitple names, having all of those names refer to the new file. When the destination of cp is a file that already exists, possibly with multiple names via hard or soft links, cp will make all of those names refer to the new file. There will be no 'orphan' references to the old file left over.
Given this command, it is pretty easy to get the 'just change the file for one name' behavior -- unlink the file first. Given just that as a primitive it would be very hard to implement the 'change all references to point to the new contents' behavior.
Of course, doing rm+cp has some race condition issues (it is two commands), which is why the install command got added in BSD unix -- it basically just does rm + cp, along with some checks to make it atomic in the rare case two people try to install to the same path simultaneously, as well as the more serious problems of someone reading from the file you're trying to install to (a problem with plain cp). Then the GNU version added options to backup the old version and various other useful bookkeeping.
An inode is a collection of metadata for a file, i.e. information about a file, in a Unix/ Unix-like filesystem. It includes permission data, last access/ modify time, file size, etc.
Notably, a file's name/ path is not part of the inode. A filename is just a human-readable identifier for an inode. A file can have one or more names, the number of which is represented in the inode by its number of "links" (hard links). The number associated with the inode, the inode number, which I believe you're interpreting as its physical location on disk, is rather simply a unique identifier for the inode. An inode does contain the location of the file on disk, but that is not the inode number.
So knowing this, the difference you're seeing is in how cp and mv function. When you cp a file you're creating a new inode with a new name and copying the contents of the old file to a new location on disk. When you mv a file all you're doing is changing one of its names. If the new name is already the name of another file, the name is disassociated with the old file (and the old file's link count is reduced by 1) and associated with the new file.
You can read more about inodes here.

How to check if a locale is UTF-8?

I'm working with Yocto to create an embedded linux distribution for an ARM device (i.MX 6Quad Processors).
I've configured the list of desired locales with the variable:
IMAGE_LINGUAS = "de-de fr-fr en-gb en-gb.iso-8859-1 en-us en-us.iso-8859-1 zh-cn"
As result I've obtained a file systems that contains the following folders:
root#lam_icu:/usr/lib/locale# cd /usr/share/locale/
root#lam_icu:/usr/share/locale# ls -la
total 0
drwxr-xr-x 6 root root 416 Nov 17 2016 .
drwxr-xr-x 30 root root 2056 Nov 17 2016 ..
drwxr-xr-x 4 root root 296 Nov 17 2016 de
drwxr-xr-x 3 root root 232 Nov 17 2016 en_GB
drwxr-xr-x 4 root root 296 Nov 17 2016 fr
drwxr-xr-x 4 root root 296 Nov 17 2016 zh_CN
and:
root#lam_icu:/usr/share/locale# cd /usr/lib/locale/
root#lam_icu:/usr/lib/locale# ls -la
total 0
drwxr-xr-x 9 root root 640 Mar 13 2017 .
drwxr-xr-x 32 root root 40000 Mar 13 2017 ..
drwxr-xr-x 3 root root 1016 Mar 13 2017 de_DE
drwxr-xr-x 3 root root 1016 Mar 13 2017 en_GB
drwxr-xr-x 3 root root 1016 Mar 13 2017 en_GB.ISO-8859-1
drwxr-xr-x 3 root root 1016 Mar 13 2017 en_US
drwxr-xr-x 3 root root 1016 Mar 13 2017 en_US.ISO-8859-1
drwxr-xr-x 3 root root 1016 Mar 13 2017 fr_FR
drwxr-xr-x 3 root root 1016 Mar 13 2017 zh_CN
Which is the encoding of all non ISO-8859-1 locales? Can I assume that "en_GB" or "en_US" use the UTF-8 encoding?
I've tried to open the "LC_IDENTIFICATION" file, the result is:
Hc�������������cEnglish locale for the USAFree Software
Foundation,
Inc.http://www.gnu.org/software/libc/bug-glibc-locales#gnu.orgEnglishUSA1.02000-06-24en_US:2000en_US:2000en_US:2000en_US:2000en_US:2000en_US:2000en_US:2000en_US:2000en_US:2000en_US:2000en_US:2000en_US:2000UTF-8
At the end of the file there is something that recalls "UTF-8". Is this enough to assume that the encoding is UTF-8?
How to check if a locale is UTF-8?
LC_IDENTIFICATION doesn't tell you much:
LC_IDENTIFICATION - this is not a user-visible category, it contains information about the locale itself and is rarely useful for users or developers (but is listed here for completeness sake).
You'd have to look at the complete set of files.
There appears to be no standard command-line utility for doing this, but there is a runtime call (added a little later than the original locale functions). Here is a sample program which illustrates the function nl_langinfo:
#include <stdio.h>
#include <locale.h>
#include <langinfo.h>
int
main(int argc, char **argv)
{
int n;
for (n = 1; n < argc; ++n) {
if (setlocale(LC_ALL, argv[n]) != 0) {
char *code = nl_langinfo(CODESET);
if (code != 0)
printf("%s ->%s\n", argv[n], code);
else
printf("?%s (nl_langinfo)\n", argv[n]);
} else {
printf("? %s (setlocale)\n", argv[n]);
}
}
return 0;
}
and some output, e.g., by foo $(locale -a):
aa_DJ ->ISO-8859-1
aa_DJ.iso88591 ->ISO-8859-1
aa_DJ.utf8 ->UTF-8
aa_ER ->UTF-8
aa_ER#saaho ->UTF-8
aa_ER.utf8 ->UTF-8
aa_ER.utf8#saaho ->UTF-8
aa_ET ->UTF-8
aa_ET.utf8 ->UTF-8
af_ZA ->ISO-8859-1
af_ZA.iso88591 ->ISO-8859-1
af_ZA.utf8 ->UTF-8
am_ET ->UTF-8
am_ET.utf8 ->UTF-8
an_ES ->ISO-8859-15
an_ES.iso885915 ->ISO-8859-15
an_ES.utf8 ->UTF-8
ar_AE ->ISO-8859-6
ar_AE.iso88596 ->ISO-8859-6
ar_AE.utf8 ->UTF-8
ar_BH ->ISO-8859-6
ar_BH.iso88596 ->ISO-8859-6
The directory names you're referring to are often (but not required) to be the same as encoding names. That is the assumption made in the example program. There was a related question in How to get terminal's Character Encoding, but it has no useful answers. One is interesting though, since it asserts that
locale charmap
will give the locale encoding. According to the standard, that's not necessarily so:
The command locale charmap gives the name used in localedef -f
However, localedef attaches no special meaning to the name given in the -f option.
localedef has a different option -u which identifies the codeset, but locale (in the standard) mentions no method for displaying this information.
As usual, implementations may (or may not) treat unspecified features in different ways. The GNU C library's documentation differs in some respects from the standard (see locale and localedef), but offers no explicit options for showing the codeset name.

gammu-smsd RunOnReceive script results exit status 2

I want to forward an SMS using gammu-smsd RunOnReceive.
That is the script I want to run (/var/spool/gammu/forward.sh) and it goes perfectly if I run it from a sudoer or using sudo -u gammu -g gammu /var/spool/gammu/forward.sh
#!/bin/bash
SMS_MESSAGES=1
for i in `seq $SMS_MESSAGES`
do
number="SMS_${i}_NUMBER"
text="SMS_${i}_TEXT"
eval "gammu-smsd-inject TEXT my_number_goes_here -text \"${!number}: ${!text}\""
done
And here is the problem I am experiencing:
Thu 2015/01/29 23:08:57 gammu-smsd[2549]: Starting run on receive: /var/spool/gammu/forward.sh IN20150130_000850_00_+37368214400_00.txt
Thu 2015/01/29 23:08:57 gammu-smsd[2154]: Process failed with exit status 2
Output of ls -l /etc/gammu-smsdrc /var/spool/gammu/ /usr/bin/gammu-smsd*:
-rw-r--r-- 1 root root 457 Jan 29 22:44 /etc/gammu-smsdrc
-rwxrwxrwx 1 root root 14336 Jun 10 2012 /usr/bin/gammu-smsd
-rwxrwxrwx 1 root root 51164 Jun 10 2012 /usr/bin/gammu-smsd-inject
-rwxrwxrwx 1 root root 9972 Jun 10 2012 /usr/bin/gammu-smsd-monitor
/var/spool/gammu/:
total 24
drwxrwxrwx 2 gammu gammu 4096 Jan 28 16:02 error
-rwxrwxrwx 1 gammu gammu 189 Jan 29 22:13 forward.sh
drwxrwxrwx 2 gammu gammu 4096 Jan 29 23:08 inbox
-rw-rw-r-- 1 gammu gammu 3702 Jan 29 23:08 log
drwxrwxrwx 2 gammu gammu 4096 Jan 29 23:07 outbox
drwxrwxrwx 2 gammu gammu 4096 Jan 29 23:07 sent
What happens if I just do ./forward.sh (not root) - so all is OK:
gammu-smsd-inject[2606]: Created outbox message OUTC20150029_231213_00_my_number_here_sms0.txt
Written message with ID /var/spool/gammu/outbox/OUTC20150029_231213_00_my_number_here_sms0.txt
Here is my /etc/gammu-smsdrc
# Configuration file for Gammu SMS Daemon
[gammu]
port = /dev/ttyUSB0
connection = at
[smsd]
service = files
logfile = /var/spool/gammu/log
debuglevel = 2
commtimeout = 1
sendtimeout = 15
statusfrequency = 0
outboxformat = unicode
transmitformat = unicode
RunOnReceive = /var/spool/gammu/forward.sh
inboxpath = /var/spool/gammu/inbox/
outboxpath = /var/spool/gammu/outbox/
sentsmspath = /var/spool/gammu/sent/
errorsmspath = /var/spool/gammu/error/
ps -fe | grep gammu:
gammu 2154 1 0 23:05 ? 00:00:02 /usr/bin/gammu-smsd --daemon --user gammu --pid /var/run/gammu-smsd.pid
cubie 2644 2403 0 23:20 pts/0 00:00:00 grep gammu
Please, help
I had the same problem and I solved it this way:
First add gammu user to sudoers, with no password:
type: $ sudo visudo
and add: gammu ALL=(ALL) NOPASSWD: ALL
Then run gammu-smsd as root user:
in /etc/init.d/gammu-smsd
change USER=gammu to USER=root
save it and don't forget to restart daemon: service gammu-smsd restart
In RunOnReceive script add sudo in front of gammu-smsd-inject:
e.g.: sudo gammu-smsd-inject TEXT my_tel_num -text "Hello world!"
I hope this will work for you too!
P.S.: I use Gammu version 1.31.90.

Code fails sometimes and runs sometimes

I am a newbie as far as node.js is concerned.I wrote the following code to pipeline two linux commands.
This is my nodejs code:
var spawn = require('child_process').spawn,
ls = spawn('ls',['-lh','/usr']),
grep = spawn('grep',['bin']);
/*
ls.stdout.on('data',function(data){
console.log('stdout: '+data);
});
*/
ls.stdout.on('data',function(data){
console.log(""+data);
grep.stdin.write(data);
});
ls.stderr.on('data',function(data){
console.log('stderr: '+data);
});
ls.on('exit',function(code){
console.log('Exit code '+code);
grep.stdin.end();
})
// ------------------------------------
grep.stdout.on('data',function(data){
console.log('stdout: '+data);
});
grep.stderr.on('data',function(data){
console.log('stderr: '+data);
});
Now this code fails sometimes and runs sometimes.I'm getting confused now.
When it fails,it says:
Exit code 0
total 160K
drwxr-xr-x 2 root root 68K Oct 12 12:54 bin
drwxr-xr-x 2 root root 4.0K Jun 20 19:58 games
drwxr-xr-x 54 root root 4.0K Sep 24 17:52 include
drwxr-xr-x 252 root root 44K Oct 2 21:53 lib
drwxr-xr-x 10 root root 4.0K Apr 28 19:16 local
drwxr-xr-x 2 root root 12K Sep 18 15:51 sbin
drwxr-xr-x 362 root root 12K Sep 28 17:58 share
drwxr-xr-x 5 root root 4.0K Jul 7 23:39 src
events.js:66
throw arguments[1]; // Unhandled 'error' event
^
Error: This socket is closed.
at Socket._write (net.js:517:19)
at Socket.write (net.js:509:15)
at Socket.<anonymous> (/home/rajat/nodexperiments/full-spawn.js:13:13)
at Socket.EventEmitter.emit (events.js:88:17)
at Pipe.onread (net.js:395:14)
And when it runs,it says:
total 160K
drwxr-xr-x 2 root root 68K Oct 12 12:54 bin
drwxr-xr-x 2 root root 4.0K Jun 20 19:58 games
drwxr-xr-x 54 root root 4.0K Sep 24 17:52 include
drwxr-xr-x 252 root root 44K Oct 2 21:53 lib
drwxr-xr-x 10 root root 4.0K Apr 28 19:16 local
drwxr-xr-x 2 root root 12K Sep 18 15:51 sbin
drwxr-xr-x 362 root root 12K Sep 28 17:58 share
drwxr-xr-x 5 root root 4.0K Jul 7 23:39 src
Exit code 0
stdout: drwxr-xr-x 2 root root 68K Oct 12 12:54 bin
drwxr-xr-x 2 root root 12K Sep 18 15:51 sbin
Any Ideas?
This is quite clearly a race condition, because node.js is a highly parallel environment you have found a really nice example to demonstrate this.
ls.on('exit',function(code){
console.log('Exit code '+code);
grep.stdin.end();
})
ls fires the above event, before it has finished writing to grep, and then closes the socket that's being used to communicate. The hint your output gives you is that the exit(0) message coming from ls appears once at the top of the output, and once just above the error message.
You shouldn't be closing grep's stdin channel here.
But what about using .exec() instead of spawn on a bash script that does something like
#/bin/bash mybashscript
ls $1 | grep bin
and then using the JavaScript closure, such as
child = exec('mybashscript',['-lh','/usr']
function (error, stdout, stderr) {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
if (error !== null) {
console.log('exec error: ' + error);
}
});
With the events your code ordering is more difficult to read.

Resources