Operating Systems (234123) – Winter-2013/14 (Homework Wet 3) Homework Wet 3 Due date: Thursday, 2.1.2014, 12:30 noon Teaching assistant in charge: Arie Tal Important: the Q&A for the exercise will take place at a public forum Piazza only. Please note, the forum is a part of the exercise. Important clarifications/corrections will also be published in the FAQ on the site. A number of guidelines to use the forum: Read previous Q&A carefully before asking the question; repeated questions will probably go without answers Be polite, remember that course staff does this as a service for the students You’re not allowed to post any kind of solution and/or source code in the forum as a hint for other students; In case you feel that you have to discuss such a matter, please come to the reception hour When posting questions regarding hw3, put them in the hw3 folder Note: Start working on the assignment as soon as possible!!! This assignment involves algorithmic design, a lot of code writing, and extensive testing and debugging. This time, for sure, there will be no postponement. 1 General Description A small company's Information Technology (IT) team decided to roll their own internal on-line group chat service. Their intent was that any employee in the company could join the chat service, receive messages posted by other employees and post their own messages. When the IT team presented their project proposal to management, management agreed to fund the project, provided that additional features were added to it. Namely, management wanted to add the capability of sending private messages between users, and the capability of querying who is currently connected to the system, as well as an indication whenever anyone joins or leaves the chat service. After the IT team went through a few use-case scenarios, they deemed it necessary to also enable anyone joining the chat service to query the recent history of messages, instead of starting a series of questions in an attempt to catch up with the current discussion. It was decided that a history of the last 100 messages should suffice. It was also agreed that private messages would not be included in the history. 1 Operating Systems (234123) – Winter-2013/14 (Homework Wet 3) 2 Detailed Description The chat service is composed of two components: 1. A central chat service server that listens to client connections, messages and queries, and broadcasts messages to clients. 2. A client that allows each employee to connect to the chat service. Each employee runs its own copy of the client component. Communication Protocol Description All communication between clients and the server must be done through named pipes (FIFOs). You must use the following naming convention for the FIFOs: “server-fifo” – the FIFO that clients use to register themselves with the server. The FIFO is created and managed by the server. A single FIFO is used for all clients to start the registration process. “fifo-<client-pid>-out” - the name of the FIFO used for messages from the client to the server, where <client-pid> is the process id of the client. “fifo-<client-pid>-in” - the name of the FIFO used for messages from the server to the client fifo-1234-in fifo-1234-out Chat Service client1 server-fifo fifo-5678-in fifo-5678-out client2 Each message sent though the FIFOs (except for server-fifo, see below) is composed of: message length – 4 bytes message – a character string of length message length Note: The message-length is in the binary representation of an int type, not a string. Also note that message is a string that is not null terminated. 2 Operating Systems (234123) – Winter-2013/14 (Homework Wet 3) Establishing a client-name When a client is run, the first argument is its client-name. For example, $ ./client client1 the client-name for the client above will be “client1”. The client-name can be any string that does not contain spaces. If no client-name parameter is supplied, the client will use “pid:<client-pid>” for its client-name, where <client-pid> is the client’s process id. Client-Server Initial Connection Protocol Upon server startup, the server must 1. Print the message Starting the chat service 2. Create the “server-fifo” FIFO and start listening for client requests The client connects to the server using the following protocol: 1. The client creates the two FIFOs fifo-<client-pid>-in, and fifo-<client-pid>-out, where <client-pid> is the client process id. 2. The client sends a single message on the server-fifo that contains its client-pid and client-name in the following format: message length – 4 bytes, the length of the client-name string client-pid – 4 bytes, in binary representation of an int type message – a character string of length message length containing the client-name 3. If there is no other client connected to the server using the same client-name, the server sends a "/ack" command to the client using the FIFO fifo-<client-pid>-in, and the connection proceeds (read below about the "/inuse" command). 4. If the connection is accepted by the server, all currently connected clients, including the joining one, must see a message: <client-name> joined the chat 5. Any subsequent communication between the client and server is done through the two FIFOs fifo-<client-pid>-in, and fifo-<client-pid>-out. 3 Operating Systems (234123) – Winter-2013/14 (Homework Wet 3) Notes: 1. If the server is not running when the client starts, the client must print a message The chat service is not available and terminate with an exit code of -1. 2. If another client with the same client-name is connected to the server, the connection will be rejected. The server must send a “/inuse” command back to the connecting client through the FIFO fifo-<client-pid>-in (see the section on "Commands” below), and the client must only print a message Client-name <client-name> in use and terminate with an exit code of -2. Replace “<client-name>” in the message with the requested client name. Client-Server Connection Termination Protocol Upon client termination due to a “/leave” command (see section on "Commands” below), end-ofinput, or SIGINT (ctrl-C),: 1. The client must send a “/leave” command to the server. 2. The server must send a "/leave_ack" command back to the originating client (and only the originating client). 3. The client must shut down gracefully, and remove the FIFOs. 4. All remaining connected clients must print a message: <client-name> left the chat Upon server termination due to SIGINT (ctrl-C): 1. The server must send a “/stopped” command to all connected clients. 2. All connected clients must print the message The chat service was stopped and exit gracefully with an exit code of 0 (cleaning up FIFOs, etc.). 3. The server must shut down gracefully, and remove the “server-fifo” FIFO. Note: Use signal(SIGNUM, <signal-handler-routine>) to replace the a default signal handler with your own. For more information regarding the signal library routine, see the man pages. 4 Operating Systems (234123) – Winter-2013/14 (Homework Wet 3) Messaging-Protocol Public messages: When a client sends a regular message, not a recognized "command” (see section on “Commands” below), the message should appear on all connected clients in the following format: <client-name>: <message content> Messages must be printed on the client as soon as they arrive. The client must not wait for any user input before printing incoming messages. A client sends a regular message by typing it and pressing Enter: <message content>↵ Note: Incoming messages from other users must be printed even while a user is typing their messages. Private messages: A client can message another client privately by mentioning the receiver's client-name at the beginning of the message as follows: @<receiver-client-name> <message content> A received private message will have the following format: <sender-client-name>: @<receiver-client-name> <message content> No other client except for the sender and the receiver shall print the message. If a client with the <receiver-client-name> is not currently connected, the client <senderclient-name> must print the message <receiver-client-name> is not connected Client Recognized Commands: /leave - notify server (by a client) of communication termination due to client leaving the chat service. /history - a client uses this command to ask the server for a history of the last 100 public messages (the /history command ignores private messages). The history of messages must be sent only to the asking client. The history is formatted as follows: 5 Operating Systems (234123) – Winter-2013/14 (Homework Wet 3) history: <client-name>: message history: <client-name>: message …. Note: /history may also print "<client-name> joined the chat" and "<client-name> left the chat" messages. “/who” - a client uses this command to get a list of clients that are currently connected to the server. The list of currently connected users must be sent only the asking client. The list of currently connected users is formatted as follows: who: <client-name-1> who: <client-name-2> … Server-side prints: When a chat server starts, it should print the message Starting the chat service When a client with client id <client-name> connects to the server, the server should print Connected to client <client-name> When a client with client name <client-name> disconnects from the server, the server should print Disconnected from client <client-name> When the server shuts down (when it is stopped, i.e. by pressing ctrl-C or sending it a SIGINT), it should print the message Stopping the chat service 6 Operating Systems (234123) – Winter-2013/14 (Homework Wet 3) An example output from three clients connected to a chat server: $ ./client Alice Connected Alice joined the chat This is a message by Alice Alice: This is a message by Alice Jane joined the chat Jane: This is a message by Jane Bob joined the chat $ ./client Jane Connected Jane joined the chat /who who: Alice who: Jane This is a message by Jane Jane: This is a message by Jane Bob joined the chat $ ./client Bob Connected Bob joined the chat /history history: Alice joined the chat history: Alice: This is a message by Alice history: Jane joined the chat history: Jane: This is a message by Jane history: Bob joined the chat $ ./server Starting the Connected to Connected to Connected to chat service client Alice client Jane client Bob 7 Operating Systems (234123) – Winter-2013/14 (Homework Wet 3) 3 Remarks 1. You should aim for maximum parallelism. Points will be deducted for “serialized” communications between clients. For example, if client A sends a private message to client B, and client C sends a private message to client D, the two should not interfere with oneanother. Hint: You should allow multiple threads to scan the list of active connections in parallel. 2. There is no requirement for messages to be printed at the exact same order in every client. However, public messages from the same client must be printed in the same order in all the connected clients. Private messages from client A to client B must be printed in client B in the order they were entered in client A. 3. For the history management, you could use either a server-side data structure, or a special history-manager “client” (that runs inside or alongside the server), or a different approach. Whichever one you choose, document the reason you chose it, and how it affects parallelism. 4. Make sure the client and server do not run into a deadlock. Points will be deducted for any deadlocks. 5. Make sure the client and server handle graceful shutdowns. Points will be deducted for segmentation violations, SIGPIPEs, crashes, etc. Make sure there are no lingering files or FIFOs after the server and clients shut down. Make sure that all clients disconnect gracefully when the server is shutdown, and that the server is aware of any client disconnecting (for any reason). Make sure that clients erase their FIFOs when they shutdown or are stopped via ctrl-C. If the client gets killed by a signal other than a SIGINT (e.g. when the window closes, or kill -9), the server should gracefully handle the situation as a regular client disconnect. In that case, it is permitted to have lingering FIFOs. Upon restarting the client with the same client-name, it must be able to connect smoothly (i.e. without being rejected by the server). 6. Avoid printing anything other than what was explicitly specified in the protocol above. Your server and client programs must implement the protocol above to the letter. If we can’t test your code with our automated testing tools, you will lose points. 7. You can add more commands between the client and server for debugging purposes, as long as they do not interfere with the implementation of the communication protocol. 8. You can assume a maximum message length of 10K. 9. Your code should build and run cleanly on the RedHat 8 Linux VM. However, you should not assume a single-core machine, and should therefore test your code on t2 (or an equivalent multi-core machine) as well. 10. Please stress-test your code. That means running your server with many client instances in different scenarios, sending hundreds of messages to one another. We will! 8 Operating Systems (234123) – Winter-2013/14 (Homework Wet 3) 4 Submission You should electronically submit a zip file that contains the source files and the Makefile. Its name should be “Makefile”. The Makefile will create two executables named “server” and “client”. You should submit a printed detailed design of your program, including explanations on the chosen algorithms, synchronization mechanisms, etc. You should also submit a printed version of the source code. Document your source code! A file named submitters.txt which includes the ID, name and email of the participating students. The following format should be used: Bill Gates bill@t2.technion.ac.il 123456789 Linus Torvalds linus@gmail.com 234567890 Steve Jobs jobs@os_is_best.com 345678901 Important Note: Make the outlined zip structure exactly. In particular, the zip should contain only the following files (no subdirectories): zipfile -+ | +- all your source/header files | +- Makefile | +- submitters.txt | +- documentation.pdf Good Luck! The Course Staff 9 Operating Systems (234123) – Winter-2013/14 (Homework Wet 3) Appendix Summary of Client Commands: Command Description /leave Client is leaving the chat service, and disconnecting from the server. /history Client asks for a history of the latest (up to 100) public messages. /who Client asks for a list of the currently connect clients (list of client-names). Summary of server commands Command Description /ack Server approved client registration, communication may proceed. /inuse Server rejected client registration. Detected an attempt to register with a clientname that is currently in use by another connected client. /leave_ack Server acknowledges a client's /leave command, and disconnects from the client. /stopped Server is shutting down. Client must gracefully disconnect and cleanup FIFOs. Summary of Client-side Messages: Message <client-name> joined the chat The chat service is not available Client-name <client-name> in use <client-name> left the chat The chat service was stopped <client-name>: <message content> <sender-client-name>: @<receiver-client-name> <message content> <receiver-client-name> is not connected history: <client-name>: message who: <client-name> Summary of Server-side Messages: Message Starting the chat service Connected to client <client-name> Disconnected from client <client-name> Stopping the chat service 10 Operating Systems (234123) – Winter-2013/14 (Homework Wet 3) A few useful tips: For stress-testing, it would be beneficial for you to create additional tools and scripts for running multiple clients with your server. One such useful tool is a "slowcat", which behaves like the "cat" command, but inserts a predetermined delay after every line that is printed (take a look at the usleep() library function man page). This should help simulate user input, and keep the client processes running long enough for them to interact with each other. To test your code more thoroughly try using random delays instead of a fixed delay One weakness of named pipes (FIFOs) is that multiple processes can write to the FIFO at the same time. This means that if you have multiple client processes all trying to identify themselves with the server, they all write to the FIFO and the server may end up getting bad messages, and will not be able to connect to clients, etc. In other words, you need a sort of "mutex" for accessing the server-fifo. One common way of achieving that is by creating a "lock" file (e.g. server-fifo.lck) by using open() with the O_CREAT and O_EXCL flags to try to create the file exclusively. The operation is "atomic" and will fail if the file already exists. Check out the man page for the open() function. To "unlock", you may simply unlink() the .lck file. You can verify some of the formatting of your output using the file "example_output.txt". 11