Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
C
cs503zchen
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Ziheng Chen
cs503zchen
Commits
9c8be518
Commit
9c8be518
authored
4 months ago
by
Ziheng Chen
Browse files
Options
Downloads
Patches
Plain Diff
Upload New File
parent
14e3c369
No related branches found
No related tags found
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
w8/rsh_server.c
+724
-0
724 additions, 0 deletions
w8/rsh_server.c
with
724 additions
and
0 deletions
w8/rsh_server.c
0 → 100644
+
724
−
0
View file @
9c8be518
#include
<sys/socket.h>
#include
<sys/wait.h>
#include
<arpa/inet.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<unistd.h>
#include
<sys/un.h>
#include
<fcntl.h>
#include
<signal.h>
#include
<pthread.h>
//INCLUDES for extra credit
//#include <signal.h>
//#include <pthread.h>
//-------------------------
#include
"dshlib.h"
#include
"rshlib.h"
typedef
struct
{
int
cli_socket
;
}
client_thread_args_t
;
// Function to handle client in a separate thread
void
*
client_handler
(
void
*
args
)
{
client_thread_args_t
*
client_args
=
(
client_thread_args_t
*
)
args
;
exec_client_requests
(
client_args
->
cli_socket
);
close
(
client_args
->
cli_socket
);
free
(
client_args
);
pthread_exit
(
NULL
);
}
/*
* start_server(ifaces, port, is_threaded)
* ifaces: a string in ip address format, indicating the interface
* where the server will bind. In almost all cases it will
* be the default "0.0.0.0" which binds to all interfaces.
* note the constant RDSH_DEF_SVR_INTFACE in rshlib.h
*
* port: The port the server will use. Note the constant
* RDSH_DEF_PORT which is 1234 in rshlib.h. If you are using
* tux you may need to change this to your own default, or even
* better use the command line override -s implemented in dsh_cli.c
* For example ./dsh -s 0.0.0.0:5678 where 5678 is the new port
*
* is_threded: Used for extra credit to indicate the server should implement
* per thread connections for clients
*
* This function basically runs the server by:
* 1. Booting up the server
* 2. Processing client requests until the client requests the
* server to stop by running the `stop-server` command
* 3. Stopping the server.
*
* This function is fully implemented for you and should not require
* any changes for basic functionality.
*
* IF YOU IMPLEMENT THE MULTI-THREADED SERVER FOR EXTRA CREDIT YOU NEED
* TO DO SOMETHING WITH THE is_threaded ARGUMENT HOWEVER.
*/
int
start_server
(
char
*
ifaces
,
int
port
,
int
is_threaded
){
int
svr_socket
;
int
rc
;
//(void)is_threaded; // Suppress unused parameter warning
//
//TODO: If you are implementing the extra credit, please add logic
// to keep track of is_threaded to handle this feature
//
svr_socket
=
boot_server
(
ifaces
,
port
);
if
(
svr_socket
<
0
){
int
err_code
=
svr_socket
;
//server socket will carry error code
return
err_code
;
}
printf
(
"Server started in %s mode.
\n
"
,
is_threaded
?
"Multi-Threaded"
:
"Single-Threaded"
);
rc
=
process_cli_requests
(
svr_socket
,
is_threaded
);
stop_server
(
svr_socket
);
return
rc
;
}
/*
* stop_server(svr_socket)
* svr_socket: The socket that was created in the boot_server()
* function.
*
* This function simply returns the value of close() when closing
* the socket.
*/
int
stop_server
(
int
svr_socket
){
return
close
(
svr_socket
);
}
/*
* boot_server(ifaces, port)
* ifaces & port: see start_server for description. They are passed
* as is to this function.
*
* This function "boots" the rsh server. It is responsible for all
* socket operations prior to accepting client connections. Specifically:
*
* 1. Create the server socket using the socket() function.
* 2. Calling bind to "bind" the server to the interface and port
* 3. Calling listen to get the server ready to listen for connections.
*
* after creating the socket and prior to calling bind you might want to
* include the following code:
*
* int enable=1;
* setsockopt(svr_socket, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
*
* when doing development you often run into issues where you hold onto
* the port and then need to wait for linux to detect this issue and free
* the port up. The code above tells linux to force allowing this process
* to use the specified port making your life a lot easier.
*
* Returns:
*
* server_socket: Sockets are just file descriptors, if this function is
* successful, it returns the server socket descriptor,
* which is just an integer.
*
* ERR_RDSH_COMMUNICATION: This error code is returned if the socket(),
* bind(), or listen() call fails.
*
*/
int
boot_server
(
char
*
ifaces
,
int
port
)
{
int
svr_socket
;
struct
sockaddr_in
server_addr
;
int
enable
=
1
;
// Step 1: Create a socket
svr_socket
=
socket
(
AF_INET
,
SOCK_STREAM
,
0
);
if
(
svr_socket
<
0
)
{
perror
(
"Error creating server socket"
);
fprintf
(
stderr
,
CMD_ERR_RDSH_COMM
);
return
ERR_RDSH_COMMUNICATION
;
}
// Step 2: Set socket options to allow address reuse
if
(
setsockopt
(
svr_socket
,
SOL_SOCKET
,
SO_REUSEADDR
,
&
enable
,
sizeof
(
int
))
<
0
)
{
perror
(
"Error setting socket options"
);
fprintf
(
stderr
,
CMD_ERR_RDSH_COMM
);
close
(
svr_socket
);
return
ERR_RDSH_COMMUNICATION
;
}
// Step 3: Prepare server address structure
memset
(
&
server_addr
,
0
,
sizeof
(
server_addr
));
server_addr
.
sin_family
=
AF_INET
;
server_addr
.
sin_port
=
htons
(
port
);
// Convert port to network byte order
// Convert and assign the interface IP
if
(
inet_pton
(
AF_INET
,
ifaces
,
&
server_addr
.
sin_addr
)
<=
0
)
{
perror
(
"Invalid server IP address"
);
fprintf
(
stderr
,
CMD_ERR_RDSH_COMM
);
close
(
svr_socket
);
return
ERR_RDSH_COMMUNICATION
;
}
// Step 4: Bind the socket
if
(
bind
(
svr_socket
,
(
struct
sockaddr
*
)
&
server_addr
,
sizeof
(
server_addr
))
<
0
)
{
perror
(
"Error binding server socket"
);
fprintf
(
stderr
,
CMD_ERR_RDSH_COMM
);
close
(
svr_socket
);
return
ERR_RDSH_COMMUNICATION
;
}
// Step 5: Put the socket in listening mode
if
(
listen
(
svr_socket
,
SOMAXCONN
)
<
0
)
{
perror
(
"Error setting server to listen mode"
);
fprintf
(
stderr
,
CMD_ERR_RDSH_COMM
);
close
(
svr_socket
);
return
ERR_RDSH_COMMUNICATION
;
}
printf
(
"Server listening on %s:%d
\n
"
,
ifaces
,
port
);
return
svr_socket
;
//return WARN_RDSH_NOT_IMPL;
}
/*
* process_cli_requests(svr_socket)
* svr_socket: The server socket that was obtained from boot_server()
*
* This function handles managing client connections. It does this using
* the following logic
*
* 1. Starts a while(1) loop:
*
* a. Calls accept() to wait for a client connection. Recall that
* the accept() function returns another socket specifically
* bound to a client connection.
* b. Calls exec_client_requests() to handle executing commands
* sent by the client. It will use the socket returned from
* accept().
* c. Loops back to the top (step 2) to accept connecting another
* client.
*
* note that the exec_client_requests() return code should be
* negative if the client requested the server to stop by sending
* the `stop-server` command. If this is the case step 2b breaks
* out of the while(1) loop.
*
* 2. After we exit the loop, we need to cleanup. Dont forget to
* free the buffer you allocated in step #1. Then call stop_server()
* to close the server socket.
*
* Returns:
*
* OK_EXIT: When the client sends the `stop-server` command this function
* should return OK_EXIT.
*
* ERR_RDSH_COMMUNICATION: This error code terminates the loop and is
* returned from this function in the case of the accept()
* function failing.
*
* OTHERS: See exec_client_requests() for return codes. Note that positive
* values will keep the loop running to accept additional client
* connections, and negative values terminate the server.
*
*/
/*
int process_cli_requests(int svr_socket) {
int cli_socket;
int rc;
while (1) {
// Step 1: Accept an incoming client connection
cli_socket = accept(svr_socket, NULL, NULL);
if (cli_socket < 0) {
perror("Error accepting client connection");
fprintf(stderr, CMD_ERR_RDSH_COMM);
return ERR_RDSH_COMMUNICATION;
}
printf("Client connected\n");
// Step 2: Handle client requests
rc = exec_client_requests(cli_socket);
// Step 3: Close client socket after handling requests
close(cli_socket);
printf(RCMD_MSG_CLIENT_EXITED);
// Step 4: If client requested server to stop, break loop
if (rc == OK_EXIT) {
printf(RCMD_MSG_SVR_STOP_REQ);
break;
}
}
return OK_EXIT;
//return WARN_RDSH_NOT_IMPL;
}
*/
int
process_cli_requests
(
int
svr_socket
,
int
is_threaded
)
{
int
cli_socket
;
int
rc
;
while
(
1
)
{
// Step 1: Accept a client connection
cli_socket
=
accept
(
svr_socket
,
NULL
,
NULL
);
if
(
cli_socket
<
0
)
{
perror
(
"Error accepting client connection"
);
fprintf
(
stderr
,
CMD_ERR_RDSH_COMM
);
return
ERR_RDSH_COMMUNICATION
;
}
//printf("Client connected\n");
printf
(
"Client connected (socket %d)
\n
"
,
cli_socket
);
fflush
(
stdout
);
// Multi-threaded mode: Create a new thread for each client
if
(
is_threaded
)
{
pthread_t
client_thread
;
client_thread_args_t
*
args
=
malloc
(
sizeof
(
client_thread_args_t
));
if
(
!
args
)
{
perror
(
"Memory allocation failed for client thread"
);
close
(
cli_socket
);
continue
;
}
args
->
cli_socket
=
cli_socket
;
if
(
pthread_create
(
&
client_thread
,
NULL
,
client_handler
,
(
void
*
)
args
)
!=
0
)
{
perror
(
"Failed to create client thread"
);
free
(
args
);
close
(
cli_socket
);
continue
;
}
// Detach thread so it cleans up after itself
pthread_detach
(
client_thread
);
}
else
{
// Single-threaded mode: Handle client in main thread
rc
=
exec_client_requests
(
cli_socket
);
close
(
cli_socket
);
printf
(
RCMD_MSG_CLIENT_EXITED
);
if
(
rc
==
OK_EXIT
)
{
printf
(
RCMD_MSG_SVR_STOP_REQ
);
break
;
}
}
}
return
OK_EXIT
;
}
/*
* exec_client_requests(cli_socket)
* cli_socket: The server-side socket that is connected to the client
*
* This function handles accepting remote client commands. The function will
* loop and continue to accept and execute client commands. There are 2 ways
* that this ongoing loop accepting client commands ends:
*
* 1. When the client executes the `exit` command, this function returns
* to process_cli_requests() so that we can accept another client
* connection.
* 2. When the client executes the `stop-server` command this function
* returns to process_cli_requests() with a return code of OK_EXIT
* indicating that the server should stop.
*
* Note that this function largely follows the implementation of the
* exec_local_cmd_loop() function that you implemented in the last
* shell program deliverable. The main difference is that the command will
* arrive over the recv() socket call rather than reading a string from the
* keyboard.
*
* This function also must send the EOF character after a command is
* successfully executed to let the client know that the output from the
* command it sent is finished. Use the send_message_eof() to accomplish
* this.
*
* Of final note, this function must allocate a buffer for storage to
* store the data received by the client. For example:
* io_buff = malloc(RDSH_COMM_BUFF_SZ);
* And since it is allocating storage, it must also properly clean it up
* prior to exiting.
*
* Returns:
*
* OK: The client sent the `exit` command. Get ready to connect
* another client.
* OK_EXIT: The client sent `stop-server` command to terminate the server
*
* ERR_RDSH_COMMUNICATION: A catch all for any socket() related send
* or receive errors.
*/
int
exec_client_requests
(
int
cli_socket
)
{
char
*
io_buff
;
ssize_t
recv_size
;
int
rc
;
// Step 1: Allocate buffer for receiving client data
io_buff
=
(
char
*
)
malloc
(
RDSH_COMM_BUFF_SZ
);
if
(
!
io_buff
)
{
fprintf
(
stderr
,
"Error: Memory allocation failed
\n
"
);
return
ERR_RDSH_COMMUNICATION
;
}
while
(
1
)
{
// Send the prompt before reading input
send_message_string
(
cli_socket
,
SH_PROMPT
);
// Step 2: Receive command from client
recv_size
=
recv
(
cli_socket
,
io_buff
,
RDSH_COMM_BUFF_SZ
-
1
,
0
);
if
(
recv_size
<
0
)
{
perror
(
"Error receiving command from client"
);
fprintf
(
stderr
,
CMD_ERR_RDSH_COMM
);
free
(
io_buff
);
return
ERR_RDSH_COMMUNICATION
;
}
// Step 3: Check for client disconnection
if
(
recv_size
==
0
)
{
printf
(
"Client disconnected
\n
"
);
free
(
io_buff
);
return
OK
;
}
// Ensure null termination of received data
io_buff
[
recv_size
]
=
'\0'
;
printf
(
RCMD_MSG_SVR_EXEC_REQ
,
io_buff
);
// Step 4: Check for built-in commands
if
(
strcmp
(
io_buff
,
"exit"
)
==
0
)
{
send_message_string
(
cli_socket
,
RCMD_MSG_CLIENT_EXITED
"
\n
"
);
free
(
io_buff
);
return
OK
;
}
if
(
strcmp
(
io_buff
,
"stop-server"
)
==
0
)
{
send_message_string
(
cli_socket
,
"dsh4> stop-server
\n
"
RCMD_MSG_SVR_STOP_REQ
"
\n
"
);
fflush
(
stdout
);
printf
(
"stopping ..."
);
usleep
(
200000
);
free
(
io_buff
);
printf
(
"stopping ..."
);
return
OK_EXIT
;
}
// Step 5: Parse and execute the command pipeline
command_list_t
clist
;
rc
=
build_cmd_list
(
io_buff
,
&
clist
);
if
(
rc
!=
OK
)
{
send_message_string
(
cli_socket
,
CMD_ERR_RDSH_EXEC
);
}
else
{
rc
=
rsh_execute_pipeline
(
cli_socket
,
&
clist
);
free_cmd_list
(
&
clist
);
}
// Step 6: Send EOF character to mark end of response
send_message_eof
(
cli_socket
);
}
// Cleanup before exiting
free
(
io_buff
);
return
OK
;
//return WARN_RDSH_NOT_IMPL;
}
/*
* send_message_eof(cli_socket)
* cli_socket: The server-side socket that is connected to the client
* Sends the EOF character to the client to indicate that the server is
* finished executing the command that it sent.
*
* Returns:
*
* OK: The EOF character was sent successfully.
*
* ERR_RDSH_COMMUNICATION: The send() socket call returned an error or if
* we were unable to send the EOF character.
*/
int
send_message_eof
(
int
cli_socket
){
int
bytes_sent
;
// Send EOF character to client
bytes_sent
=
send
(
cli_socket
,
&
RDSH_EOF_CHAR
,
1
,
0
);
if
(
bytes_sent
<
0
)
{
perror
(
"Error sending EOF to client"
);
fprintf
(
stderr
,
CMD_ERR_RDSH_COMM
);
return
ERR_RDSH_COMMUNICATION
;
}
return
OK
;
//return WARN_RDSH_NOT_IMPL;
}
/*
* send_message_string(cli_socket, char *buff)
* cli_socket: The server-side socket that is connected to the client
* buff: A C string (aka null terminated) of a message we want
* to send to the client.
*
* Sends a message to the client. Note this command executes both a send()
* to send the message and a send_message_eof() to send the EOF character to
* the client to indicate command execution terminated.
*
* Returns:
*
* OK: The message in buff followed by the EOF character was
* sent successfully.
*
* ERR_RDSH_COMMUNICATION: The send() socket call returned an error or if
* we were unable to send the message followed by the EOF character.
*/
int
send_message_string
(
int
cli_socket
,
char
*
buff
){
int
bytes_sent
,
msg_length
;
// Validate input
if
(
!
buff
)
{
fprintf
(
stderr
,
"Error: Null buffer passed to send_message_string()
\n
"
);
return
ERR_RDSH_COMMUNICATION
;
}
msg_length
=
strlen
(
buff
);
// Get message length
// Step 1: Send the message
bytes_sent
=
send
(
cli_socket
,
buff
,
msg_length
,
0
);
if
(
bytes_sent
<
0
)
{
perror
(
"Error sending message to client"
);
fprintf
(
stderr
,
CMD_ERR_RDSH_COMM
);
return
ERR_RDSH_COMMUNICATION
;
}
// Check for partial send
if
(
bytes_sent
!=
msg_length
)
{
fprintf
(
stderr
,
CMD_ERR_RDSH_SEND
,
bytes_sent
,
msg_length
);
return
ERR_RDSH_COMMUNICATION
;
}
if
(
msg_length
==
0
)
{
return
send_message_eof
(
cli_socket
);
// Ensure EOF is always sent
}
// Step 2: Send EOF character
return
send_message_eof
(
cli_socket
);
//return WARN_RDSH_NOT_IMPL;
}
/*
* rsh_execute_pipeline(int cli_sock, command_list_t *clist)
* cli_sock: The server-side socket that is connected to the client
* clist: The command_list_t structure that we implemented in
* the last shell.
*
* This function executes the command pipeline. It should basically be a
* replica of the execute_pipeline() function from the last deliverable.
* The only thing different is that you will be using the cli_sock as the
* main file descriptor on the first executable in the pipeline for STDIN,
* and the cli_sock for the file descriptor for STDOUT, and STDERR for the
* last executable in the pipeline. See picture below:
*
*
*┌───────────┐ ┌───────────┐
*│ cli_sock │ │ cli_sock │
*└─────┬─────┘ └────▲──▲───┘
* │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
* │ │ Process 1 │ │ Process 2 │ │ Process N │ │ │
* │ │ │ │ │ │ │ │ │
* └───▶stdin stdout├─┬──▶│stdin stdout├─┬──▶│stdin stdout├──┘ │
* │ │ │ │ │ │ │ │ │
* │ stderr├─┘ │ stderr├─┘ │ stderr├─────┘
* └──────────────┘ └──────────────┘ └──────────────┘
* WEXITSTATUS()
* of this last
* process to get
* the return code
* for this function
*
* Returns:
*
* EXIT_CODE: This function returns the exit code of the last command
* executed in the pipeline. If only one command is executed
* that value is returned. Remember, use the WEXITSTATUS()
* macro that we discussed during our fork/exec lecture to
* get this value.
*/
int
rsh_execute_pipeline
(
int
cli_sock
,
command_list_t
*
clist
)
{
int
num_cmds
=
clist
->
num
;
int
prev_pipe
=
-
1
;
int
fd
[
2
];
pid_t
pids
[
num_cmds
];
for
(
int
i
=
0
;
i
<
num_cmds
;
i
++
)
{
if
(
i
<
num_cmds
-
1
&&
pipe
(
fd
)
<
0
)
{
perror
(
"pipe"
);
fprintf
(
stderr
,
CMD_ERR_RDSH_EXEC
);
return
ERR_RDSH_CMD_EXEC
;
}
pids
[
i
]
=
fork
();
if
(
pids
[
i
]
<
0
)
{
perror
(
"fork"
);
fprintf
(
stderr
,
CMD_ERR_RDSH_EXEC
);
return
ERR_RDSH_CMD_EXEC
;
}
if
(
pids
[
i
]
==
0
)
{
// Child Process
if
(
i
==
0
)
{
// First command: Redirect stdin from the client socket
dup2
(
cli_sock
,
STDIN_FILENO
);
}
else
{
// Redirect stdin from the previous pipe
dup2
(
prev_pipe
,
STDIN_FILENO
);
close
(
prev_pipe
);
}
if
(
i
<
num_cmds
-
1
)
{
// Not the last command: Redirect stdout to the pipe
dup2
(
fd
[
1
],
STDOUT_FILENO
);
close
(
fd
[
1
]);
}
else
{
// Last command: Redirect stdout and stderr to client socket
dup2
(
cli_sock
,
STDOUT_FILENO
);
dup2
(
cli_sock
,
STDERR_FILENO
);
}
// Close all open file descriptors
if
(
i
>
0
)
{
close
(
prev_pipe
);
}
if
(
i
<
num_cmds
-
1
)
{
close
(
fd
[
0
]);
close
(
fd
[
1
]);
// Ensure both pipe ends are closed
}
execvp
(
clist
->
commands
[
i
].
argv
[
0
],
clist
->
commands
[
i
].
argv
);
perror
(
"execvp failed"
);
fprintf
(
stderr
,
CMD_ERR_RDSH_EXEC
);
exit
(
ERR_RDSH_CMD_EXEC
);
}
else
{
// Parent Process
if
(
i
>
0
)
close
(
prev_pipe
);
if
(
i
<
num_cmds
-
1
)
{
prev_pipe
=
fd
[
0
];
close
(
fd
[
1
]);
}
}
}
// Wait for all children to finish
int
status
;
for
(
int
i
=
0
;
i
<
num_cmds
;
i
++
)
{
waitpid
(
pids
[
i
],
&
status
,
0
);
}
// Send EOF to indicate command completion
send_message_eof
(
cli_sock
);
return
WEXITSTATUS
(
status
);
//return WARN_RDSH_NOT_IMPL;
}
/************** OPTIONAL STUFF ***************/
/****
**** NOTE THAT THE FUNCTIONS BELOW ALIGN TO HOW WE CRAFTED THE SOLUTION
**** TO SEE IF A COMMAND WAS BUILT IN OR NOT. YOU CAN USE A DIFFERENT
**** STRATEGY IF YOU WANT. IF YOU CHOOSE TO DO SO PLEASE REMOVE THESE
**** FUNCTIONS AND THE PROTOTYPES FROM rshlib.h
****
*/
/*
* rsh_match_command(const char *input)
* cli_socket: The string command for a built-in command, e.g., dragon,
* cd, exit-server
*
* This optional function accepts a command string as input and returns
* one of the enumerated values from the BuiltInCmds enum as output. For
* example:
*
* Input Output
* exit BI_CMD_EXIT
* dragon BI_CMD_DRAGON
*
* This function is entirely optional to implement if you want to handle
* processing built-in commands differently in your implementation.
*
* Returns:
*
* BI_CMD_*: If the command is built-in returns one of the enumeration
* options, for example "cd" returns BI_CMD_CD
*
* BI_NOT_BI: If the command is not "built-in" the BI_NOT_BI value is
* returned.
*/
Built_In_Cmds
rsh_match_command
(
const
char
*
input
)
{
(
void
)
input
;
// Suppress unused parameter warning
return
BI_NOT_IMPLEMENTED
;
}
/*
* rsh_built_in_cmd(cmd_buff_t *cmd)
* cmd: The cmd_buff_t of the command, remember, this is the
* parsed version fo the command
*
* This optional function accepts a parsed cmd and then checks to see if
* the cmd is built in or not. It calls rsh_match_command to see if the
* cmd is built in or not. Note that rsh_match_command returns BI_NOT_BI
* if the command is not built in. If the command is built in this function
* uses a switch statement to handle execution if appropriate.
*
* Again, using this function is entirely optional if you are using a different
* strategy to handle built-in commands.
*
* Returns:
*
* BI_NOT_BI: Indicates that the cmd provided as input is not built
* in so it should be sent to your fork/exec logic
* BI_EXECUTED: Indicates that this function handled the direct execution
* of the command and there is nothing else to do, consider
* it executed. For example the cmd of "cd" gets the value of
* BI_CMD_CD from rsh_match_command(). It then makes the libc
* call to chdir(cmd->argv[1]); and finally returns BI_EXECUTED
* BI_CMD_* Indicates that a built-in command was matched and the caller
* is responsible for executing it. For example if this function
* returns BI_CMD_STOP_SVR the caller of this function is
* responsible for stopping the server. If BI_CMD_EXIT is returned
* the caller is responsible for closing the client connection.
*
* AGAIN - THIS IS TOTALLY OPTIONAL IF YOU HAVE OR WANT TO HANDLE BUILT-IN
* COMMANDS DIFFERENTLY.
*/
Built_In_Cmds
rsh_built_in_cmd
(
cmd_buff_t
*
cmd
)
{
(
void
)
cmd
;
// Suppress unused parameter warning
return
BI_NOT_IMPLEMENTED
;
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment