I am trying to write a simple server program in Ocaml that communicates with other processes via a socket. I currently have a problem that the strings the server reads (with input_line ic) do not seem to compare with other strings properly. For example, I run the server program and use telnet to connect to it, if I send "end" as a line, the server program trys to match with "end", but this doesn't work as expected. The function that handles communication is service (below), which is called to handle a client as part of a forking server (something like the double fork treatment here).
let service ic oc
try while true do
let instr = input_line ic in
match instr with
| "end" -> print_endline "matching end" (* this never runs *)
| _ -> output_string oc ((String.uppercase instr) ^ "\n") ; flush oc
done
with End_of_file -> print_endline "Input stream ended."
;;
In fact, if I do print_endline (string_of_bool ("end" = instr)) I always get false (even when I send "end" via telnet). To try and get some sense of what is going I printed out the result of different comparison operations between the let-binding and the try block:
print_endline instr ;
print_endline "end" ;
print_endline (string_of_bool ("end" = instr)) ;
print_endline (string_of_bool ("end" == instr)) ;
print_endline (string_of_int (String.compare "end" instr)) ;
When I send "end" the server now prints out
end
end
false
false
-1
I'm really lost as to what could be going on - I presume it must be something about getting the instr via reading from a socket, as usually you can compare strings just fine.
I don't think I actually had a question in all that background so here are a few variants that could work:
What am I doing wrong?
Why can't I test the input in order to take different actions?
Is this a bug in Ocaml?
Do you need the complete source to figure this out?
My guess is that there are carriage returns in the strings coming in from telnet. As I recall, the old protocols tend to send CRLF at the ends of lines.
You might try printing the string out using String.escaped.
It's pretty unlikely you're seeing a bug in OCaml.
Related
Thanks to this great answer from #Przemyslaw Szufel, we can easily redirect stdio output into a file providing its path.
I wanted to know if it is possible to both redirect stio output to a file and still continue print it in the terminal. That is probably an easy feature to achieve but I could not find it!
And obviously not running the code twice :)
You need a Tee Stream such as https://github.com/fredrikekre/TeeStreams.jl/.
Unfortunately the interface of redirect_stdio does not accept abstract IO for its sink. It requires it to be an IOStream which is a concrete Julia type.
So it seems like there is no good way with the redirect_stdio via streams.
However redirect_stdio does accept sockets and named pipes and this is a way you could consider (yet too complex for this simple tasks - maybe someone finds a more elegant solution).
First you need to create a pipe server that will be running asynchronously in your REPL session (this is code for Windows for Linux you will probably need to remove \\.\pipe\ from the pipe name):
using Sockets
srv = Sockets.listen(raw"\\.\pipe\TeePipe")
mystdout = stdout
#async begin
open("log.txt", "w") do f
sock = accept(srv)
while !eof(sock)
line = readline(sock)
println(mystdout, line)
flush(mystdout)
println(f, line)
flush(f)
end
end
end
Now at the same REPL session you create the client:
teelogger = Sockets.connect(raw"\\.\pipe\TeePipe")
And here is a sample REPL session showing that this works:
julia> redirect_stdio(stdout=teelogger) do
println("Line1")
println("Line2")
#show 3+4
end
Line1
Line2
3 + 4 = 7
7
shell> more log.txt
Line1
Line2
3 + 4 = 7
I'm attempting to write a single bpftrace script which grab the strings passing from a postfix process and a saslauthd for the authentication part. The goal is detect compromise account of my company. The strace command give me some good results:
strace -p PID -s 100 -e 'read'
read(7, "\0\20", 2) = 2
read(7, "xxxxxxxxxx", 10) = 10
read(7, "\0\t", 2) = 2
read(7, "YYYYYYYYY", 9) = 9
read(7, "\0\4", 2) = 2
read(7, "smtp", 4) = 4
I can recover login/password and detect if there is a bruteforce running.
but I try to have the same results with bpftrace with:
$ bpftrace -e 'kprobe:sys_read /comm=="saslauthd"/ {printf("%<%s>\n",str(arg1,arg2));}'
<>
<login>
<>
<>
<>
<smtp>
In this case, I can read some sys_read syscall strings but not all. I don't understand why my bpftrace doesn't have the same result.
I also think about the null character and that why i use str(arg1,arg2) to force the size of the array. I'v also tried to use tracepoint and this is the same result.
Maybe someone can help me to understand where is my error ? So any input will be appreciated
TL;DR. That's actually the expected behavior of str(buf, len). It retrieves the string pointed to by buf, with a limit to len characters including the NULL character. Thus, since in your case some strings start with a NULL character, str() will copy an empty string.
Sources. bpftrace translates str() into a call to the BPF_FUNC_probe_read_str BPF helper. In the kernel, that helper itself calls strncpy_from_unsafe.
I don't think bpftrace already has a function implementing what you're looking for. If you want your described semantics, you could ask for a copy() function in bpftrace. Though, looking at the commit that introduced str(), it shouldn't be too hard to write a patch for that. Don't hesitate to send a pull request!
I wrote a shell script to collect data on each character of the alphabet from a large table. As a result of the function being memory intensive, I'd like to partition the characters of the alphabet so that each character is concurrently called using a different port number. However I can't seem to successfully pass the desired command line argument into my function. Testing on a single port and a small table, what I've tried is the following...
On the server: I set up my dummy table and defined a function...
ts:([]sym:1000?`A`Ab`B`Bc`C`Ca`X`Xz`Y`Yx`Z`Zy;price:1000?100.0;num:til 1000)
collect:{[x;y]select from x where sym like y}
On the client: I open a connection handle, use the .z.X namespace to return strings of the raw, unfiltered command line arguments, index into it and store as a variable, and then attempt to pass that variable into my server side function. I've tried storing as a character and a symbol. It will run without error but neither returns any data when called. I save this file as collector.q..
/ start connection handles to remote server
h:hopen `::5000
/ index into command line arguments to get partition letter. Store as character and symbol
part:.z.X[6]
symPart:`$part
/ call server side functions
fetched:h (`collect; `ts; "symPart*")
/ close connection handle
hclose h
my shell script looks like the following...
#!/bin/sh
port=$1
partition=$2
for x in {A..Z}
do
echo running partition $x on port $port
$QHOME/l64/q collector.q -p $port -partition $x > ./stdout$port.log 2>&1 &
port=$(($port + 1))
done
After running the shell script, when calling the fetched function on the client, what's returned is an empty table...
q)fetched
symbol price ID
---------------
q)
I think the first issue is that you are not getting the correct value for part from the command line. .z.X returns the command line as a list of tokens while .z.x returns the same but without the q command and file name.
q test.q -p 5000 -partition a
q).z.X
"/opt/kdb/3.5/l32/q"
"test.q"
"-p"
"5000"
"-partition"
,"a"
q).z.X[6]
""
Use .Q.opt function to turn the command line parameters into a dictionary that much more reliable.
q)params:.Q.opt .z.X
q)params
p | "5000"
partition| ,"a"
q)`$first params`partition
`a
The second issue is that "symPart*" won't evaluate the variable symPart as it is inside quotes. All kdb+ sees is a string. Each loop of the bash script will send the same command (`collect; `ts; "symPart*") over the handle, with the collect checking against a "symPart" partition which I guess doesn't exist.
To pass it in as a variable you can change it to symPart,"*", but in this case symPart needs to be a string, not a symbol so you can remove the casting.
You can amend your script as follows:
/ start connection handles to remote server
h:hopen `::5000
/ convert command line arguments into dictionary and index to get partition letter
part:first .Q.opt[.z.X]`partition;
/ call server side functions
fetched:h (`collect; `ts; symPart,"*")
/ close connection handle
hclose h
Or to do this all in a single q script you could do the following:
/ start connection handles to remote server
h:hopen `::5000
/ call server side functions
fetched:.Q.a!h each (`collect; `ts),/: enlist each .Q.a,\:"*"
/ close connection handle
hclose h
.Q.a holds all the lowercase letters in a string.
q).Q.a
"abcdefghijklmnopqrstuvwxyz"
We can create all the wildcards using the each left \: with , (join) to join each of them to "*" and then create multiple commands to send over the handle using each right /: to join the unchanging left hand side to all of the different wildcards.
q).Q.a,\:"*"
"a*"
"b*"
"c*"
"d*"
..
q)(`collect; `ts),/: enlist each .Q.a,\:"*"
`collect `ts "a*"
`collect `ts "b*"
`collect `ts "c*"
..
Note the output of fetched will be a dictionary with keys as the partitions and the fetched result of each partition as the corresponding value.
GCC version 4.6
The Problem: To find a way to feed in parameters to the executable, say a.out, from the command line - more specifically feed in an array of double precision numbers.
Attempt: Using the READ(*,*) command, which is older in the standard:
Program test.f -
PROGRAM MAIN
REAL(8) :: A,B
READ(*,*) A,B
PRINT*, A+B, COMMAND_ARGUMENT_COUNT()
END PROGRAM MAIN
The execution -
$ gfortran test.f
$ ./a.out 3.D0 1.D0
This did not work. On a bit of soul-searching, found that
$./a.out
3.d0,1.d0
4.0000000000000000 0
does work, but the second line is an input prompt, and the objective of getting this done in one-line is not achieved. Also the COMMAND_ARGUMENT_COUNT() shows that the numbers fed into the input prompt don't really count as 'command line arguments', unlike PERL.
If you want to get the arguments fed to your program on the command line, use the (since Fortran 2003) standard intrinsic subroutine GET_COMMAND_ARGUMENT. Something like this might work
PROGRAM MAIN
REAL(8) :: A,B
integer :: num_args, ix
character(len=12), dimension(:), allocatable :: args
num_args = command_argument_count()
allocate(args(num_args)) ! I've omitted checking the return status of the allocation
do ix = 1, num_args
call get_command_argument(ix,args(ix))
! now parse the argument as you wish
end do
PRINT*, A+B, COMMAND_ARGUMENT_COUNT()
END PROGRAM MAIN
Note:
The second argument to the subroutine get_command_argument is a character variable which you'll have to parse to turn into a real (or whatever). Note also that I've allowed only 12 characters in each element of the args array, you may want to fiddle around with that.
As you've already figured out read isn't used for reading command line arguments in Fortran programs.
Since you want to read an array of real numbers, you might be better off using the approach you've already figured out, that is reading them from the terminal after the program has started, it's up to you.
The easiest way is to use a library. There is FLAP or f90getopt available. Both are open source and licensed under free licenses.
The latter is written by Mark Gates and me, just one module and can be learned in minutes but contains all what is needed to parse GNU- and POSIX-like command-line options. The first is more sophisticated and can be used even in closed-source projects. Check them out.
Furthermore libraries at https://fortranwiki.org/fortran/show/Command-line+arguments
What READ (*,*) does is that it reads from the standard input. For example, the characters entered using the keyboard.
As the question shows COMMAND_ARGUMENT_COUNT() can be used to get the number of the command line arguments.
The accepted answer by High Performance Mark show how to retrieve the individual command line arguments separated by blanks as individual character strings using GET_COMMAND_ARGUMENT(). One can also get the whole command line using GET_COMMAND(). One then has to somehow parse that character-based information into the data in your program.
I very simple cases you just need the program requires, for example, two numbers, so you read one number from arg 1 and another form arg 2. That is simple. Or you can read a triplet of numbers from a single argument if they are comma-separated like 1,2,3 using a simple read(arg,*) nums(1:3).
For general complicated command line parsing one uses libraries such as those mentioned in the answer by Hani. You have set them up so that the library knows the expected syntax of the command line arguments and the data it should fill with the values.
There is a middle ground, that is still relatively simple, but one already have multiple arguments, that correspond to Fortran variables in the program, that may or may not be present. In that case one can use the namelist for the syntax and for the parsing.
Here is an example, the man point is the namelist /cmd/ name, point, flag:
implicit none
real :: point(3)
logical :: flag
character(256) :: name
character(1024) :: command_line
call read_command_line
call parse_command_line
print *, point
print *, "'",trim(name),"'"
print *, flag
contains
subroutine read_command_line
integer :: exenamelength
integer :: io, io2
command_line = ""
call get_command(command = command_line,status = io)
if (io==0) then
call get_command_argument(0,length = exenamelength,status = io2)
if (io2==0) then
command_line = "&cmd "//adjustl(trim(command_line(exenamelength+1:)))//" /"
else
command_line = "&cmd "//adjustl(trim(command_line))//" /"
end if
else
write(*,*) io,"Error getting command line."
end if
end subroutine
subroutine parse_command_line
character(256) :: msg
namelist /cmd/ name, point, flag
integer :: io
if (len_trim(command_line)>0) then
msg = ''
read(command_line,nml = cmd,iostat = io,iomsg = msg)
if (io/=0) then
error stop "Error parsing the command line or cmd.conf " // msg
end if
end if
end subroutine
end
Usage in bash:
> ./command flag=T name=\"data.txt\" point=1.0,2.0,3.0
1.00000000 2.00000000 3.00000000
'data.txt'
T
or
> ./command flag=T name='"data.txt"' point=1.0,2.0,3.0
1.00000000 2.00000000 3.00000000
'data.txt'
T
Escaping the quotes for the string is unfortunately necessary, because bash eats the first quotes.
I have written a shebang R script and would like to execute it from a Perl script. I currently use system ($my_r_script_path, $r_script_arg1, $r_script_arg2, ...) and my question is how can I verify the R script terminates normally (no errors or warnings).
guess I should make my R script return some true value at the end, only if everything is OK, then catch this value in Perl, but I'm not sure how to do that.
Thanks!
You can set the return value in the command quit(), eg q(status=1). Default is 0, see also ?quit. How to catch that one in Perl, is like catching any other returning value in Perl. It is saved in a special variable $? if I remember right. See also the examples in the perldoc for system, it should be illustrated there.
On a sidenote, I'd just use the R-Perl interface. You can find info and examples here :
http://www.omegahat.org/RSPerl/
Just for completeness :
At the beginning of your script, you can put something like :
options(
warn=2, # This will change all warnings into errors,
# so warnings will also be handled like errors
error= quote({
sink(file="error.txt"); # save the error message in a file
dump.frames();
print(attr(last.dump,"error.message"));
sink();
q("no",status=1,FALSE) # standard way for R to end after errors
})
)
This will save the error message, and break out of the R session without saving, with exit code 1 and without running the .Last.
Still, the R-Perl interface offers a lot more possibilities that are worth checking out if you're going to do this more often.