How to properly pass buffer pointers to Linux system calls in x86_64 assembly? - linux

Hello I am trying to learn how to make system calls with x86_64 assembly on Linux. I am running into an issue where I can't seem to figure out how to properly pass the argument for getpeername.
In this link using C it looks like they are using the address of operator to pass the arguments. I can't figure out how to replicate this out in assembly. Here is the strace when I use no brackets for my buffer.
First I defined my buffer in the section .data
ip_buff: times 14 db 0
.length: equ $-ip_buff
This is a macro
%define SYS_getpeername 52
r12 stores the return value from the socket accept call
syscall getpeername,r12,ip_buff,15
Here is the strace not using brackets
[pid 749] accept(3, NULL, NULL <unfinished ...>
[pid 761] read(4, "", 1024) = 0
[pid 761] write(1, "", 0) = 0
[pid 761] getpeername(4, 0x600733, 0xf) = -1 EFAULT (Bad address)
Here is the strace for when I do use brackets.
[pid 749] accept(3, NULL, NULL <unfinished ...>
[pid 745] read(4, "GET / HTTP/1.1\r\nHost: 127.0.0.1:"..., 1024) = 78
[pid 745] write(1, "GET / HTTP/1.1\r\nHost: 127.0.0.1:"..., 78) = 78
[pid 745] getpeername(4, NULL, 0xf) = -1 EFAULT (Bad address)
How can I properly make this system call?

The actual problem is not with the buffer but with its length. Notice in the prototype you have socklen_t *addrlen so that should be a pointer. The value 15 that you pass is not a pointer hence the -EFAULT.
You should change the .length: equ $-ip_buff to ip_length: dd $-ip_buff and then use syscall getpeername,r12,ip_buff,ip_length

Related

How does gdb start an assembly compiled program and step one line at a time?

Valgrind says the following on their documentation page
Your program is then run on a synthetic CPU provided by the Valgrind core
However GDB doesn't seem to do that. It seems to launch a separate process which executes independently. There's also no c library from what I can tell. Here's what I did
Compile using clang or gcc gcc -g tiny.s -nostdlib (-g seems to be required)
gdb ./a.out
Write starti
Press s a bunch of times
You'll see it'll print out "Test1\n" without printing test2. You can also kill the process without terminating gdb. GDB will say "Program received signal SIGTERM, Terminated." and won't ever write Test2
How does gdb start the process and have it execute only one line at a time?
.text
.intel_syntax noprefix
.globl _start
.p2align 4, 0x90
.type _start,#function
_start:
lea rsi, [rip + .s1]
mov edi, 1
mov edx, 6
mov eax, 1
syscall
lea rsi, [rip + .s2]
mov edi, 1
mov edx, 6
mov eax, 1
syscall
mov eax, 60
xor edi, edi
syscall
.s1:
.ascii "Test1\n"
.s2:
.ascii "Test2\n"
starti implementation
As usual for a process that wants to start another process, it does a fork/exec, like a shell does. But in the new process, GDB doesn't just make an execve system call right away.
Instead, it calls ptrace(PTRACE_TRACEME) to wait for the parent process to attach to it, so GDB (the parent) is already attached before the child process makes an execve() system call to make this process start executing the specified executable file.
Also note in the execve(2) man page:
If the current program is being ptraced, a SIGTRAP signal is sent
to it after a successful execve().
So that's how the kernel debugging API supports stopping before the first user-space instruction is executed in a newly-execed process. i.e. exactly what starti wants. This doesn't depend on setting a breakpoint; that can't happen until after execve anyway, and with ASLR the correct address isn't even known until after execve picks a base address. (GDB by default disables ASLR, but it still works if you tell it not to disable ASLR.)
This is also what GDB use if you set breakpoints before run, manually, or by using start to set a one-time breakpoint on main. Before the starti command existed, a hack to emulate that functionality was to set an invalid breakpoint before run, so GDB would stop on that error, giving you control at that point.
If you strace -f -o gdb.trace gdb ./foo or something, you'll see some of what GDB does. (Nested tracing apparently doesn't work, so running GDB under strace means GDB's ptrace system call fails, but we can see what it does leading up to that.)
...
231566 execve("/usr/bin/gdb", ["gdb", "./foo"], 0x7ffca2416e18 /* 57 vars */) = 0
# the initial GDB process is PID 231566.
... whole bunch of stuff
231566 write(1, "Starting program: /tmp/foo \n", 28) = 28
231566 personality(0xffffffff) = 0 (PER_LINUX)
231566 personality(PER_LINUX|ADDR_NO_RANDOMIZE) = 0 (PER_LINUX)
231566 personality(0xffffffff) = 0x40000 (PER_LINUX|ADDR_NO_RANDOMIZE)
231566 vfork( <unfinished ...>
# 231584 is the new PID created by vfork that would go on to execve the new PID
231584 openat(AT_FDCWD, "/proc/self/fd", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 13
231584 newfstatat(13, "", {st_mode=S_IFDIR|0500, st_size=0, ...}, AT_EMPTY_PATH) = 0
231584 getdents64(13, 0x558403e20360 /* 16 entries */, 32768) = 384
231584 close(3) = 0
... all these FDs
231584 close(12) = 0
231584 getdents64(13, 0x558403e20360 /* 0 entries */, 32768) = 0
231584 close(13) = 0
231584 getpid() = 231584
231584 getpid() = 231584
231584 setpgid(231584, 231584) = 0
231584 ptrace(PTRACE_TRACEME) = -1 EPERM (Operation not permitted)
231584 write(2, "warning: ", 9) = 9
231584 write(2, "Could not trace the inferior pro"..., 37) = 37
231584 write(2, "\n", 1) = 1
231584 write(2, "warning: ", 9) = 9
231584 write(2, "ptrace", 6) = 6
231584 write(2, ": ", 2) = 2
231584 write(2, "Operation not permitted", 23) = 23
231584 write(2, "\n", 1) = 1
# gotta love unbuffered stderr
231584 exit_group(127) = ?
231566 <... vfork resumed>) = 231584 # in the parent
231584 +++ exited with 127 +++
# then the parent is running again
231566 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=231584, si_uid=1000, si_status=127, si_utime=0, si_stime=0} ---
231566 rt_sigreturn({mask=[]}) = 231584
... then I typed "quit" and hit return
There some earlier clone system calls to create more threads in the main GDB process, but those didn't exit until after the vforked PID that attempted ptrace(PTRACE_TRACEME). They were all just threads since they used clone with CLONE_VM. There was one earlier vfork / execve of /usr/bin/iconv.
Annoyingly, modern Linux has moved to PIDs wider than 16-bit so the numbers get inconveniently large for human minds.
step implementation:
Unlike stepi which would use PTRACE_SINGLESTEP on ISAs that support it (e.g. x86 where the kernel can use the TF trap flag, but interestingly not ARM), step is based on source-level line number <-> address debug info. That's usually pointless for asm, unless you want to step past macro expansions or something.
But for step, GDB will use ptrace(PTRACE_POKETEXT) to write an int3 debug-break opcode over the first byte of an instruction, then ptrace(PTRACE_CONT) to let execution run in the child process until it hits a breakpoint or other signal. (Then put back the original opcode byte when this instruction needs to execute). The place at which it puts that breakpoint is something it finds by looking for the next address of a line-number in the DWARF or STABS debug info (metadata) in the executable. That's why only stepi (aka si) works when you don't have debug info.
Or possibly it would use PTRACE_SINGLESTEP one or two times as an optimization if it saw it was close.
(I normally only use si or ni for debugging asm, not s or n. layout reg is also nice, when GDB doesn't crash. See the bottom of the x86 tag wiki for more GDB asm debugging tips.)
If you meant to ask how the x86 ISA supports debugging, rather than the Linux kernel API which exposes those features via a target-independent API, see the related Q&As:
How is PTRACE_SINGLESTEP implemented?
Why Single Stepping Instruction on X86?
How to tell length of an x86-64 instruction opcode using CPU itself?
Also How does a debugger work? has some Windowsy answers.

lazyness, does words read (within interact) all the input, or only what is needed?

I am starting to learn Haskell, and I am trying to understand
how much work do the functions do (specially with respect to
the laziness concept). Please see the following program:
main::IO()
main = interact ( head . words)
Will this program read all the input or only the first word in input?
Just the first word:
% yes | ghc -e 'interact (head . words)'
y
%
But beware: this relies a feature called "lazy IO" that is only kind of related to the technique of laziness in pure code. Pure functions are lazy by default and you must work hard to make them strict; IO is "strict IO" by default and you must work hard to make it lazy IO. A handful of library functions (notably interact, (h)getContents, and readFile) have gone to this effort.
It also has some problems with composability.
Conceptually, it reads only what it needs. But it probably uses a buffer to do so:
$ yes | strace -feread,write ghc -e 'interact (head . words)'
...
[pid 61274] read(0, "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"..., 8096) = 8096
[pid 61272] write(1, "y", 1y) = 1
[pid 61272] --- SIGVTALRM {si_signo=SIGVTALRM, si_code=SI_TIMER, si_timerid=0, si_overrun=0, si_value={int=0, ptr=0}} ---
[pid 61272] write(5, "\376", 1) = 1
[pid 61273] read(4, "\376", 1) = 1
[pid 61273] +++ exited with 0 +++
[pid 61274] +++ exited with 0 +++
[pid 61276] +++ exited with 0 +++
+++ exited with 0 +++
This shows (on a Linux system) that the program split itself into multiple threads, one of them read 8KiB of data from stdin, then another output the first word. The main reason is that repeatedly reading small amounts is quite inefficient. Asynchronous sources like terminals and sockets may produce smaller amounts of data, though:
$ strace -f -e trace=read,write -e signal= ghc -e 'interact (head . words)'
...
hello program
Process 61594 attached
[pid 61592] read(0, "hello program\n", 8096) = 14
[pid 61590] write(1, "hello", 5hello) = 5
In this case, the terminal layer completed the read at the first newline, even though the buffer was still 8KiB large. As this was enough data for the first word to be identified, no further reads were needed.

Interprocess communication via Pipes

It is known that during Interprocess Communication in Linux, the processes communicate with each other through a special file named as "Pipe".
It is also known that the the operations performed on that file is write by one process and read by one process in order to communicate with each other.
Now, the question is :
Do these write and read operations are performed in parallel during the communication (operations are executed parallely) ? and if not than,
What happens when one of the process enters the SLEEP state during the communication? Does it performs the write operation first for the second process to read or it goes directly to sleep without performing any of the write and read operation?
The sending process can write until the pipe buffer is full (64k on Linux since 2.6.11). After that, write(2) will block.
The receiving process will block until data is available to read(2).
For a more detailed look into pipe buffering, look at https://unix.stackexchange.com/a/11954.
For example, this program
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char wbuf[32768];
char buf[16384];
/* Initialize writer buffer with 012...89 sequence */
for (int i = 0; i < sizeof(wbuf); i++)
wbuf[i] = '0' + i % 10;
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, sizeof(buf)) > 0);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else { /* Parent writes sequence to pipe */
close(pipefd[0]); /* Close unused read end */
for (int i = 0; i < 5; i++)
write(pipefd[1], wbuf, sizeof(wbuf));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
will produce the following sequence when run with gcc pipes.c && strace -e trace=open,close,read,write,pipe,clone -f ./a.out:
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
close(3) = 0
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320\3\2\0\0\0\0\0"..., 832) = 832
close(3) = 0
pipe([3, 4]) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f32117489d0) = 21114
close(3) = 0
write(4, "01234567890123456789012345678901"..., 32768) = 32768
write(4, "01234567890123456789012345678901"..., 32768) = 32768
write(4, "01234567890123456789012345678901"..., 32768strace: Process 21114 attached
<unfinished ...>
[pid 21114] close(4) = 0
[pid 21114] read(3, "01234567890123456789012345678901"..., 16384) = 16384
[pid 21114] read(3, <unfinished ...>
[pid 21113] <... write resumed> ) = 32768
[pid 21114] <... read resumed> "45678901234567890123456789012345"..., 16384) = 16384
[pid 21113] write(4, "01234567890123456789012345678901"..., 32768 <unfinished ...>
[pid 21114] read(3, "01234567890123456789012345678901"..., 16384) = 16384
[pid 21114] read(3, <unfinished ...>
[pid 21113] <... write resumed> ) = 32768
[pid 21114] <... read resumed> "45678901234567890123456789012345"..., 16384) = 16384
[pid 21113] write(4, "01234567890123456789012345678901"..., 32768 <unfinished ...>
[pid 21114] read(3, "01234567890123456789012345678901"..., 16384) = 16384
[pid 21114] read(3, <unfinished ...>
[pid 21113] <... write resumed> ) = 32768
[pid 21114] <... read resumed> "45678901234567890123456789012345"..., 16384) = 16384
[pid 21113] close(4) = 0
[pid 21114] read(3, "01234567890123456789012345678901"..., 16384) = 16384
[pid 21114] read(3, "45678901234567890123456789012345"..., 16384) = 16384
[pid 21114] read(3, "01234567890123456789012345678901"..., 16384) = 16384
[pid 21114] read(3, "45678901234567890123456789012345"..., 16384) = 16384
[pid 21114] read(3, "", 16384) = 0
[pid 21114] close(3) = 0
[pid 21114] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=21114, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
You'll notice that the reads and writes are interleaved and that the writing and reading processes will block a few times as either the pipe is full or not enough data is available for reading.

why does shell save, dup, and restore redirected descriptor in the parent, rather than redirect in the child?

I wanted to understand how redirection is implemented in a (POSIX) shell, so I looked at dash (the simplest shell) code, and here is how it works.
A dash script like:
date > foobar.txt
date
is (as an SSCCE) handled like this:
int fd;
int saved;
fd = open64("foobar.txt", O_WRONLY|O_CREAT);
saved = fcntl(1, F_DUPFD, 10);
dup2(fd, 1);
if (!fork()) {
execl("/bin/date", "date", (char *)NULL);
}
dup2(saved, 1);
if (!fork()) {
execl("/bin/date", "date", (char *)NULL);
}
This is strange. Why save, dup and dup again to restore, descriptors in the parent, when it would be much simpler to just dup in the child, and not have to save and restore. This is simpler and I checked it works the same:
int fd;
if (!fork()) {
fd = open64("foobar.txt", O_WRONLY|O_CREAT);
dup2(fd, 1);
execl("/bin/date", "date", (char *)NULL);
}
if (!fork()) {
execl("/bin/date", "date", (char *)NULL);
}
I am sure there must be a good reason and I am not understanding something deeper. What is it?
No good reason as far as I can tell. Bash does things in the opposite order, and the externally observable behavior is the same.
I didn't bother reading the source code, it's easy enough to see what happens using strace. (The : is to prevent the shell from optimizing away the fork.)
$ strace -fetrace=dup2,file,process dash -c 'date > foobar.txt; :'
execve("/usr/bin/dash", ["dash", "-c", "date > foobar.txt; :"], [/* 71 vars */]) = 0
open("foobar.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
dup2(3, 1) = 1
stat("/usr/bin/date", {st_mode=S_IFREG|0755, st_size=105280, ...}) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f6650bf2750) = 1948
strace: Process 1948 attached
[pid 1947] wait4(-1, <unfinished ...>
[pid 1948] execve("/usr/bin/date", ["date"], [/* 71 vars */]) = 0
[pid 1948] exit_group(0) = ?
[pid 1948] +++ exited with 0 +++
<... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 1948
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=1948, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
dup2(10, 1) = 1
exit_group(0) = ?
+++ exited with 0 +++
$ strace -fetrace=dup2,file,process bash -c 'date > foobar.txt; :'
execve("/usr/bin/bash", ["bash", "-c", "date > foobar.txt; :"], [/* 71 vars */]) = 0
stat("/usr/bin/date", {st_mode=S_IFREG|0755, st_size=105280, ...}) = 0
access("/usr/bin/date", R_OK) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fd5b78349d0) = 2026
strace: Process 2026 attached
[pid 2025] wait4(-1, <unfinished ...>
[pid 2026] open("foobar.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
[pid 2026] dup2(3, 1) = 1
[pid 2026] execve("/usr/bin/date", ["date"], [/* 71 vars */]) = 0
[pid 2026] exit_group(0) = ?
[pid 2026] +++ exited with 0 +++
<... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 2026
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=2026, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, 0x7ffeffe03c50, WNOHANG, NULL) = -1 ECHILD (No child processes)
exit_group(0) = ?
+++ exited with 0 +++

sails lift causes error ENOENT (No such file or Directory)

Im trying to launch this project https://github.com/lucj/sails-oauth2-api to learn from it however im having trouble getting it to work.
I installed the prerequisites with:
npm install
in the terminal at the project directory
when i do:
strace -f -e trace=process sails lift
it gives me the following debugging output.
execve("/usr/bin/sails", ["sails", "lift"], [/* 64 vars */]) = 0
arch_prctl(ARCH_SET_FS, 0x7f4017319740) = 0
execve("/usr/local/sbin/node", ["node", "/usr/bin/sails", "lift"], [/* 64 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/local/bin/node", ["node", "/usr/bin/sails", "lift"], [/* 64 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/sbin/node", ["node", "/usr/bin/sails", "lift"], [/* 64 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/bin/node", ["node", "/usr/bin/sails", "lift"], [/* 64 vars */]) = 0
arch_prctl(ARCH_SET_FS, 0x7f204315b780) = 0
clone(child_stack=0x7f2043172f70, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CL ONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f20431739d0, tls=0x7f2043173700, child_tidptr=0x7f20431739d0) = 5575
Process 5575 attached
[pid 5574] clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f204315ba50) = 5576
Process 5576 attached
[pid 5576] execve("/usr/bin/nodejs", ["/usr/bin/nodejs", "/home/vern/sails-oauth2- api-deve"..., "default", "--gdsrc=/home/vern/sails-oauth2-"..., "--environment=development", "--baseurl=http://localhost:1337", "--signalpath=/___signal"], [/* 66 vars */]) = 0
[pid 5576] arch_prctl(ARCH_SET_FS, 0x7fc1da6b1780) = 0
[pid 5576] clone(child_stack=0x7fc1da6c8f70, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CL ONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7fc1da6c99d0, tls=0x7fc1da6c9700, child_tidptr=0x7fc1da6c99d0) = 5577
Process 5577 attached
[pid 5574] clone(child_stack=0x7f2041b39f70, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CL ONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f2041b3a9d0, tls=0x7f2041b3a700, child_tidptr=0x7f2041b3a9d0) = 5578
[pid 5574] clone(child_stack=0x7f2041338f70, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CL ONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f20413399d0, tls=0x7f2041339700, child_tidptr=0x7f20413399d0) = 5579
[pid 5574] clone(child_stack=0x7f2040b37f70, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CL ONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f2040b389d0, tls=0x7f2040b38700, child_tidptr=0x7f2040b389d0) = 5580
Process 5580 attached
Process 5579 attached
Process 5578 attached
[pid 5574] clone(child_stack=0x7f203bffef70, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CL ONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f203bfff9d0, tls=0x7f203bfff700, child_tidptr=0x7f203bfff9d0) = 5581
Process 5581 attached
trustedTestClient already exists
- client_id: NC2OKCDJAI
- client_secret: JNRL6Rlw6NDPEXPBFgFRkQ4EybQYFu
- redirectURI: http://localhost:1338
untrustedTestClient already exists
- client_id: TR5U2NKLJO
- client_secret: hq0Qgm2NpQ6KRbd91Dwy7Ao1Jlcv6E
- redirectURI: http://localhost:1339
info:
info:
info: Sails.js <|
info: v0.9.11 |\
info: /|.\
info: / || \
info: ,' |' \
info: .-'.-==|/_--'
info: `--'-------'
info: __---___--___---___--___---___--___
info: ____---___--___---___--___---___--___-__
info:
info: Server lifted in `/home/vern/sails-oauth2-api-develop`
info: To see your app, visit http://localhost:1337
info: To shut down Sails, press <CTRL> + C at any time.
debug: --------------------------------------------------------
debug: :: Fri Jun 13 2014 05:00:49 GMT-0700 (PDT)
debug:
debug: Environment : development
debug: Port : 1337
debug: --------------------------------------------------------
debug: Lowering sails...
[pid 5576] --- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=5574, si_uid=1000} ---
[pid 5577] +++ killed by SIGHUP +++
events.js:72
throw er; // Unhandled 'error' event
^
Error: ENOENT, open 'logs/application.log'
[pid 5579] _exit(0) = ?
[pid 5580] _exit(0) = ?
[pid 5581] _exit(0) = ?
[pid 5580] +++ exited with 0 +++
[pid 5581] +++ exited with 0 +++
[pid 5579] +++ exited with 0 +++
[pid 5576] +++ killed by SIGHUP +++
[pid 5574] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_KILLED, si_pid=5576, si_status=SIGHUP, si_utime=86, si_stime=26} ---
[pid 5578] _exit(0) = ?
[pid 5574] exit_group(8) = ?
[pid 5575] +++ exited with 8 +++
[pid 5578] +++ exited with 8 +++
+++ exited with 8 +++
The top 6 lines mean something but i dont know what it means and how i need to fix it. I have a selfmade project that works perfectly when i use sails lift.
P.S. The project supposedly created 2 clients which i can find back in the debug output but the application crashes later on.
So its something with this project that causes the problem but what?
It says Error: ENOENT, open 'logs/application.log'.
This means it's trying to open logs/application.log but that it doesn't exist. Try creating the file and lifting again:
mkdir logs && cd $_ && touch application.log && cd .. && sails lift

Resources