COMP3301 2024 Assignment 2
COMP3301 Assignment 2
IPC Memory Sharing - All on the SameCM Page
Due: 3pm Monday in Week 10 (30th of September)
Submission: Blackboard (reflection) and Git.
Code submission is marked in your prac session in week 10
Last Updated: September 12, 2024
1 Academic Integrity
All assessments are individual. You should feel free to discuss aspects of C programming and
assessment specifications with fellow students and discuss the related APIs in general terms.
You should not actively help (or seek help from) other students with the actual coding of your
assessment. It is cheating to look at another student’s code, and it is cheating to allow your
code to be seen or shared in printed or electronic form. You should note that all submitted
code will be subject to automated checks for plagiarism and collusion. If we detect plagiarism
or collusion (outside of the base code given to everyone), formal misconduct proceedings will be
initiated against you. If you’re having trouble, seek help from a teaching staff member. Do not
be tempted to copy another student’s code. You should read and understand the statements
on student misconduct in the course profile and on the school website: https://eecs.uq.edu.
au/current-students/guidelines-and-policies-students/student-conduct
1.1 Use of AI Tools
All assessment tasks evaluate students’ abilities, skills and knowledge without the aid of gen-
erative Artificial Intelligence (AI) or Machine Translation (MT). Students are advised that the
use of AI technologies to develop responses (e.g. code generation) is strictly prohibited and
may constitute student misconduct under the Student Code of Conduct.
2 Background
Two processes can communicate via file descriptors (e.g., pipes, open files) or shared memory
(e.g., memory mapped using mmap() with MAP_SHARED). However, the file descriptors or shared
memory can only be inherited when there is a parent-child relationship between the processes.
For example, open a file or create a shared memory mapping, and then fork, after which both
the parent and the child can access the same file descriptors or shared memory.
This becomes an issue when two unrelated processes wish to communicate via file descrip-
tors or shared memory. UNIX allows the passing of file descriptors between any two processes
via UNIX-domain sockets using the socket control message SCM_RIGHTS.
The man page for UNIX(4) provides the following description:
SOCK_STREAM and SOCK_SEQPACKET sockets also support the communication of
UNIX file descriptors through the use of the msg_control field in the msg
argument to sendmsg(2) and recvmsg(2).
Page 1 of 15
COMP3301 2024 Assignment 2
Any valid descriptor may be sent in a message. The file descriptor(s) to
be passed are described using a struct cmsghdr that is defined in the
include file
. The type of the message is SCM_RIGHTS, and
the data portion of the messages is an array of integers representing the
file descriptors to be passed. The number of descriptors being passed is
defined by the length field of the message; the length field is the sum
of the size of the header plus the size of the array of file descriptors.
The received descriptor is a duplicate of the sender's descriptor, as if
it were created with a call to dup(2). Per-process descriptor flags, set
with fcntl(2), are not passed to a receiver. Descriptors that are
awaiting delivery, or that are purposely not received, are automatically
closed by the system when the destination socket is closed.
Currently, SCM_RIGHTS is the only UNIX socket control message supported by OpenBSD (more
are supported by other Unix-like operating systems, such as Linux). The sharing of memory
between two processes could also be implemented via socket control messages, however this is
feature is currently not in OpenBSD.
3 Overview
This assignment has three main tasks:
• Find where SCM_RIGHTS is implemented in the kernel and understand how it works.
• Implement an inter-process communication mechanism for sharing memory pages via the
socket control message interface, similar to SCM_RIGHTS.
• Identify at least one potential security flaw in the way this task is designed or in the code
that you have written, and talk about how you have mitigated the risk or how the risk
could be mitigated.
The memory sharing must be done via the socket control message SCM_PAGES defined in the
base code patch and implemented yourself, and behave in an identical manner to SCM_RIGHTS.
4 Instructions
To complete the assignment, you will need to do the following:
1. Download the base code patch
cd ~
ftp https://stluc.manta.uqcloud.net/comp3301/public /2024/ comp3301 -2024-
a2.patch
2. Create the a2 branch
cd /usr/src
git checkout -b a2 openbsd -7.5
Page 2 of 15
COMP3301 2024 Assignment 2
3. Apply the base code patch
git am < ~/comp3301 -2024 -a2.patch
4. Install the includes
cd /usr/src/include
doas make includes
5. Build the kernel
cd /usr/src/sys/arch/amd64/compile/GENERIC.MP
make obj
make config
make -j4
doas make install
6. Reboot
doas reboot
7. Build and install a2client
cd /usr/src/usr.sbin/a2client
make obj
make
doas make install
8. Build and install a2server
cd /usr/src/usr.sbin/a2server
make obj
make
doas make install
9. Build and install fdsend (optional)
cd /usr/src/usr.sbin/fdsend
make obj
make
doas make install
10. Build and install fdrecv (optional)
cd /usr/src/usr.sbin/fdrecv
make obj
make
doas make install
5 Your Tasks
Note: Various sections of this specification sheet are intentionally vague to provide you with
more freedom and to test your abilities to make design decisions. You may freely decide on any-
thing not explicitly stated in this specification sheet. However, your decisions must be reasonable
(i.e., you can justify it during the demo).
Page 3 of 15
COMP3301 2024 Assignment 2
5.1 Understanding SCM_RIGHTS
Read and understand how SCM_RIGHTS is implemented in OpenBSD 7.5 and how file descriptors
are passed between processes. You are required to fill out and submit a flow chart template
provided to you on Blackboard. The completed flow chart is to be submitted as a part of your
reflection.
You must also, in your reflection, include the steps you took to find the relevant code in the ker-
nel source tree and list the utilities which you have used to accomplish that (e.g., grep/fgrep,
cscope), if applicable.
5.2 Implement SCM_PAGES
The base code patch defines SCM_PAGES, the structure struct memshareblk, and some flags in
the header file .
struct memshareblk {
void *msb_addr; /* starting address of the mem block */
size_t msb_len; /* length of the memory block to share */
int msb_flags; /* memory protection options flags */
int msb_prot; /* protection flags if MSB_NEW_RROT is used */
};
#define MSB_SAME_PROT 0x01 /* use same protection flags as sender */
#define MSB_NEW_PROT 0x02 /* use protection flags specified in msb_prot */
#define SCM_PAGES 0x08 /* memory pages (memshareblk array) */
Implement the SCM_PAGES interface so that instead of sending an array of file descriptors, you
send an array of one or more memshareblk structures. You do not have to consider the case of
multiple control messages being sent together in one sendmsg() syscall.
Each memshareblk structure represents a list of consecutive pages mapped into memory that
are to be shared or have been received, with the first one being mapped at the memory address
specified by the msb_addr field. The msb_len field specifies the number of bytes to share or
the number of bytes received, which can be used to derive the number of pages to share or have
been received via the formula NumPages = ⌈msb_len ÷ PAGE_SIZE⌉.
Example 1:
msb_addr = 0x123456789000
msb_len = 100
Number of pages: ⌈100 ÷ 4096⌉ = 1
Starting address: 0x123456789000
List of pages: [0x123456789000]
Example 2:
Page 4 of 15
COMP3301 2024 Assignment 2
msb_addr = 0xDEADBEEF0000
msb_len = 12345
Number of pages: ⌈12345 ÷ 4096⌉ = 4
Starting address: 0xDEADBEEF0000
List of pages: [0xDEADBEEF0000, 0xDEADBEEF1000, 0xDEADBEEF2000, 0xDEADBEEF3000]
Virtual Addresses
The address(es) of the page(s) mapped into the receiver’s address space do not need to match
that of the sender. This means if a page is mapped at 0x123456789000 in the sender’s address
space, it does not need to come out at 0x123456789000 in the receiver’s address space. It could
be mapped at any address. This behaviour is consistent with that of SCM_RIGHTS - there is
no guarantee that the file descriptors would have the same numbers between the sender and
receiver either.
If multiple consecutive pages are to be sent using the same memshareblk structure (i.e., msb_-
len is greater than PAGE_SIZE), you must ensure that they are also consecutive when they are
mapped into the receiver’s address space.
Memory Protection
The memshareblk structure defines two fields for memory protection - msb_flags and msb_-
prot. If the flag MSB_SAME_PROT is specified in msb_flags, then the memory protection shall
be preserved when page(s) are shared, that is the receiver should have the same access to the
page(s) as the sender. If the flag MSB_NEW_PROT is specified in msb_flags, the memory protec-
tion flags for all the page(s) being shared shall be set to msb_prot for the receiver.
If both or none of MSB_SAME_PROT and MSB_NEW_PROT flags are specified, the sendmsg() syscall
should fail with EINVAL.
Hints
A few important things (or edge cases) to consider have been left unspecified or specified
vaguely, for example:
• What if the address specified in the memshareblk structure does not point to the start of
a page?
• What if the address specified in the memshareblk structure does not even have page(s)
mapped?
• What if there is a hole/gap in the list of pages to send?
• What if the sender unmaps the page(s) before the receiver receives it/them?
• What if the sender exits before the receiver receives the page(s)?
The ones listed above are only the most obvious ones. There are more which have not been
specified, and you are expected to handle them in reasonable (i.e., justifiable) ways if you wish
to score higher marks. For example, the most logical way and the way in which you are expected
to handle the first dot-point would be to return EFAULT or EINVAL.
Page 5 of 15
COMP3301 2024 Assignment 2
5.3 Identify Security Risk(s)
In your reflection, you will need to identify at least one potential security risk associated with
how this task is defined or how you have implemented the task and explain the risk in detail.
You are also required explain how this risk could be mitigated.
A description of the risk shall be submitted as a part of your reflection.
Hints
The following are all considered as security risks:
• Denial of service (e.g., OS crashes, slowdowns)
• Leakage of confidential information
• Leakage of kernel data structures to userland
6 Misc. Requirements
6.1 Code Style
Your code is to be written according to OpenBSD’s style guide, as per the style(9) man page.
An automatic tool for checking for style violations is available at:
https://stluc.manta.uqcloud.net/comp3301/public/2022/cstyle.pl.
Code style marks will be calculated based on the number of style violations in the code which
you have written yourself or modified - style violations in the OpenBSD source tree or in the
base code will not affect code style marks. Some level of functionality is required to score marks
for code style (i.e., no submission implies no style violations, however no style marks will be
awarded in that case).
6.2 Reliability, Robustness, Portability and Modularity
In order to score higher marks, your code is expected to be reliable and robust, that is it should
handle all errors appropriately and should not crash unexpectedly. Your code should also be
portable and modular, in the sense that constants such as page sizes should not be hard coded
and similar code should not be duplicated in multiple areas.
6.3 Compilation
Your code must be compile-able under the GENERIC.MP (generic multiprocessor) configuration
for the AMD64 (aka x86-64 and x64) architecture. Code with compile-time errors will be
marked manually, and may result in heavy penalties.
7 Git Submission
Submission must be made electronically by committing and pushing your changes to your
course-provided Git repository on source.eait.uq.edu.au. Code checked into any other
Page 6 of 15
COMP3301 2024 Assignment 2
branch in your repository or not pushed to the a2 branch (i.e., left on your VM) will not
be marked.
As per the source.eait.uq.edu.au usage guidelines, you should only commit source code
and makefiles. Please do not commit cscope outputs, core dumps or base code patch files.
Your a2 branch should consist of:
• The OpenBSD 7.5 base commit (provided)
• The A2 base patch commit (provided)
• Commit(s) for adding the required functionality (by yourself)
Commit history and commit messages do not contribute to your grade. However, it is strongly
recommended that you commit frequently and use appropriate commit messages. You are
kindly reminded that this is a professional academic assignment, and therefore, inappropriate
commit messages (i.e., profanity) may result in the deduction of style marks.
7.1 Reflection
Reflect on your implementation by:
1. Completing the SCM_RIGHTS flow chart.
2. Identifying and describing at least one security risk.
Your reflection should contain the following sections:
1. Introduction
2. Flowchart - Two templates are given, you must complete either A or B. Both flowcharts
are worth the same number of marks.
3. Security Risk(s)
4. Conclusion
Additional sections or subsections can be added. Upload your reflection as a PDF file to the
Blackboard A2 reflection submission. Page length is a maximum of 4 pages or less. PDF
name must be your STUDENT NUMBER a2.pdf. Note this is your XXXXXXXX ID
number and not your sXXXXXXX login.
7.2 Marking
Your submission will be marked by course staff during an in-person demo with you at your prac
session of the due week. You must attend your session in person; otherwise, your submission
will not be marked. Online attendance, e.g., Zoom, is not permitted.
Page 7 of 15
COMP3301 2024 Assignment 2
7.3 Demo Session
You will be asked to demonstrate your assignment as part of your regular practical session.
Demonstrations will run on a marking VM (not your VM), which has nothing but the latest
code that you have pushed to the a2 branch before the submission deadline. Manual and au-
tomated tests and manual code examinations will be conducted during the demo. Changes to
your code during the session will not be accepted.
It is your responsibility to ensure that your code compiles and operates correctly. Code that
fails to compile or code that crashes the kernel upon boot results in your submission not being
marked for functionality.
You will be asked to explain certain aspects of your submission, including but not limited
to:
• How does SCM_RIGHTS work in the kernel?
• How did you find the location of the relevant code?
• Why have you labeled your flowchart as such?
• Why did you do ... in the code?
• What does ... do?
• What happens if I run/do this?
• Is this going to succeed or fail?
• Have you considered ... ?
• What are some of the security risks?
• How could it potentially be exploited?
• How did you mitigate it?
Most of the questions will be open-ended and have no single correct answer. However, failure
to demonstrate your understanding or to justify your design decisions may result in penalties
for the functionality marks of the relevant categories.
Failure to prove that you have a sufficient understanding of the code that you have presumably
written or a wrong understanding of the concepts yet correct code may result in allegations of
academic misconduct.
8 Recommendations
8.1 Backups
It is highly recommended that you use Git to regularly backup your assignment code. Beware
that corruptions to your file-system can happen in the event of kernel crashes, and will re-
quire you to start from scratch if there is no backup. Regularly committing code to Git and
Page 8 of 15
COMP3301 2024 Assignment 2
pushing to origin ensures that your code will not be lost in the event of a file-system corruption.
It is also recommended that you take a snapshot of your virtual machine (VM) before you
attempt the programming aspect of this assignment. This can be done by running snapshot
in your VM Control.
8.2 Relevant Manual Pages
The following manual pages may be useful for the understanding of existing functionalities or
the implementation of the assignment. You are not required or expected to use all of these in
the code which you write.
• CMSG DATA(3) - Socket Control Message Routines
• uvm(9) - Unified Virtual Memory
• malloc(9) - Kernel Memory Allocator
• TAILQ INIT(3) - Doubly-Linked List Macros
• RB PROTOTYPE(3) - Red-Black Tree Macros
• file(9) - File Descriptor Overview
• KASSERT(9) - Kernel Assert Routines
8.3 Testing
Public tests are provided for this assignment. It is suggested that you run the command sync
before running any of the tests (or any test program which you write), to minimise the chance
of file system corruption in the event of an unexpected kernel crash.
The public tests only test the basic functionalities of SCM_PAGES, and therefore your imple-
mentation is not guaranteed to be fully correct even if you pass all the public tests. You are
encouraged to write your own test cases.
Installation
The public tests can be installed by running the following commands in your VM:
cd ~
ftp https://stluc.manta.uqcloud.net/comp3301/public /2024/ a2_public_tests.tar
tar -xvf a2_public_tests.tar
cd a2test
make obj
make
doas make install
cd tests
make
doas make install
This will install the test program a2test and 22 public tests.
Page 9 of 15
COMP3301 2024 Assignment 2
a2test Test Program
The a2test program takes the following command-line arguments:
a2test
The first argument must be one of public, private, custom, or all. public tells the test
program to search for public tests, private tells the test program to search for private tests
(which you do not have access to), custom tells the test program to search for custom tests
(which you can write yourself), and all tells the test program to search for all tests.
The second argument must be one of run, info, or dump. run executes the selected test(s),
info prints out the information about the selected test(s), and dump dumps your output of
the selected test(s) to _your_output.out in the current directory, where is the
name of the test.
The third argument is a list of tests to run/info/dump, or all to run/info/dump all tests.
The list is comma separated.
Example Test Commands
To run:
• All test, you run a2test all run all.
• A particular test (e.g., 3.2), you run a2test all run 3.2.
• A selection of tests (e.g., 2.5, 3.1 and 4.4), you run a2test all run 2.5,3.1,4.4.
To display the information about:
• All tests, you run a2test all info all.
• A particular test (e.g., 3.5), you run a2test all info 3.5.
• A selection of tests (e.g., 1.3, 2.2 and 4.5), you run a2test all run 1.3,2.2,4.5.
To dump your outputs to the current directory for:
• All tests, you run a2test all dump all.
• A particular test (e.g., 2.4), you run a2test all dump 2.4.
• A selection of tests (e.g., 1.1, 3.3, 5.2), you run a2test all dump 1.1,3.3,5.2.
Examples of passed and failed tests with screenshots are in the A2 Tests thread on Ed.
Page 10 of 15
COMP3301 2024 Assignment 2
9 Specification Clarifications
Specification clarifications and/or updates may be issued, they will be communicated through
Blackboard and/or Ed. If needed, you are encouraged to seek for spec clarifications in the A2
Spec Clarification Megathread on Ed rather than by making individual posts or emailing the
teaching staff.
All clarifications made since the initial release of this specification are coloured red in this
document.
10 Appendix
10.1 SCM_RIGHTS (File Descriptor Passing) Examples
The base code patch includes a sample file descriptor sender program named fdsend and a
sample file descriptor receiver program named fdrecv. The snippets shown in this section are
taken from those programs. You may freely use and/or modify them to help you understand
how socket control messages work - fdsend and fdrecv are not a part of the assignment and
therefore will not be marked.
Sender
Say that the sender wishes to share some file descriptors with the receiver, and has them in an
array defined like this (this could be an array of a single file descriptor, or an array of multiple
file descriptors):
int fds[];
This array of file descriptors will be sent via a socket control message (SCM) to the receiver,
which consists of a control message header followed by a data area. The control message can
be defined and allocated like this:
// Define a buffer large enough to hold the control message which will
// be sent. This control message will contain the control message header
// and control message data (array of file descriptors). CMSG_SPACE()
// calculates the total size, taking alignment of both the header and
// data into account.
char cmsgbuff[CMSG_SPACE(sizeof(fds))];
It is defined in the specification that at least one byte of real data must be sent, in order
for ancillary data (control messages) to be sent. The real data could be anything, and is
encapsulated via I/O vectors (iovec). For this example, the integer 3301 is sent as the real
data:
// Must send at least one byte of real data in order to send ancillary
// data. The data is sent via an I/O vector here.
int number = 3301;
struct iovec iov;
iov.iov_base = &number;
iov.iov_len = sizeof(number);
Page 11 of 15
COMP3301 2024 Assignment 2
A message header of type struct msghdr which contains the control message(s) and the real
data would have to be set up next. The fields msg_name and msg_namelen are optional and
will not be used in this example. The field msg_flags is ignored and therefore not initialised.
struct msghdr msg;
// The name (address) field(s) are optional, and therefore set to NULL
// here.
msg.msg_name = NULL;
msg.msg_namelen = 0;
// Fill in the I/O vector here. In this case, we only have a single I/O
// vector, so msg_iov would just point to our I/O vector and msg_iovlen
// would just be 1.
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
// Fill in the pointer to the first control message (ancillary data)
// and its length.
msg.msg_control = cmsgbuff;
// This is required for CMSG_FIRSTHDR() to function, set it to the size
// of the first control message.
msg.msg_controllen = sizeof(cmsgbuff);
The control message buffer must be zeroed to ensure the correct operation of CMSG_NEXTHDR(),
however if only a single control message is to be sent, this is not required as CMSG_NEXTHDR()
would not need to be used.
// The control message buffer must be zeroed in order to ensure the
// correct operation of CMSG_NXTHDR(). In this case, we are sending
// only one single control message, and thus there is no strict
// requirement for this buffer to be zeroed.
memset(cmsgbuff, 0, sizeof(cmsgbuff));
Once the message header is set up, the control message can be filled out. In the case of SCM_-
RIGHTS, the control message must have the field cmsg_level set to SOL_SOCKET and the field
cmsg_type set to SCM_RIGHTS. The field cmsg_len should be set to CMSG_LEN() of the size of
the control message data (file descriptor array), which is the aligned size of the control message
header plus the raw size of the control message data.
// Obtain the pointer to the first control message header and fill out
// the required fields.
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(fds));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
Once the control message structure is filled out, the actual data to transfer (array of file descrip-
tors) is copied into the data area pointed to by the return value of CMSG_DATA(). The pointer
returned by CMSG_DATA() cannot be dereferenced directly, and therefore memcpy is required if
the data is to be accessed.
Page 12 of 15
COMP3301 2024 Assignment 2
// Copy the array of file descriptors into the data area of the
// control message.
memcpy(CMSG_DATA(cmsg), &fds, sizeof(fds));
If more control messages are to be sent at once, the subsequent cmsghdr structures could be
accessed using CMSG_NXTHDR() (which is not demonstrated here, as only one control message
is to be sent).
Once the control messages are set up, the mgs_controllen field of the message header is
to be updated with the sum of CMSG_SPACE() on the data length of each control message.
In the case of this example, there is only one control message and therefore mgs_controllen
is simply set to the CMSG_SPACE() of the data length of the first and only control message -
CMSG_SPACE(sizeof(fds)).
// Set the final length which is the sum of the size of all control
// messages (in this case, the first and only one).
msg.msg_controllen = CMSG_SPACE(sizeof(fds));
At this point, everything has been set up and the control message(s) along with the real data
can be sent out. This is done via the syscall sendmsg().
ssize_t size = sendmsg(sockfd, &msg, 0);
if (size == -1) {
fprintf(stderr, "sendmsg() failed with errno = %d\n", errno);
exit(-1);
}
printf("Sent message of size %zd.\n", size);
printf("Number sent is: %d.\n", number);
printf("File descriptors sent:\n");
for (size_t i = 0; i < (sizeof(fds) / sizeof(fds[0])); i++) {
printf(" %d\n", fds[i]);
}
Receiver
The receiver receives the real message data and the control messages(s) in a similar way to how
they are sent. A control message buffer must be set up for receiving the control message, an I/O
vector is needed for receiving the real data, and a msghdr structure is defined and populated
to point to the buffers for the control messages(s) and real data.
int fds[];
char cmsgbuff[CMSG_SPACE(sizeof(fds))];
int number;
struct iovec iov;
iov.iov_base = &number;
iov.iov_len = sizeof(number);
struct msghdr msg;
Page 13 of 15
COMP3301 2024 Assignment 2
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsgbuff;
msg.msg_controllen = sizeof(cmsgbuff);
Then the message header is passed into the syscall recvmsg() to have the real data and control
messages filled in based on the data sent by the sender. This is done like this:
ssize_t size = recvmsg(sockfd, &msg, 0);
if (size == -1) {
fprintf(stderr, "recvmsg() failed with errno = %d\n", errno);
exit(-1);
}
After we have received the data and control message(s), we could copy the file descriptors out
and do whatever we want with them.
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
memcpy(&fds, CMSG_DATA(cmsg), sizeof(fds));
printf("Received message of size %zd.\n", size);
if (size) {
printf("Received number is: %d.\n", number);
printf("File descriptors received:\n");
for (size_t i = 0; i < (sizeof(fds) / sizeof(fds[0])); i++) {
printf(" %d\n", fds[i]);
}
}
10.2 SCM_PAGES (Memory Page Sharing) Examples
SCM_PAGES works in the exact same way as SCM_RIGHTS, as specified earlier. This means the
only difference between SCM_PAGES and SCM_RIGHTS is instead of sending/receiving integer file
descriptors, you send/receive memshareblk structures which contains information about the
memory to be shared.
Two sample programs, a2client and a2server are included in the base code patch, for you to
experiment with and test the most basic functionalities of SCM_PAGES. As with the SCM_SHARE
test programs, they are not a part of the assignment and you may modify them however you
like.
a2server
This is a simple server which accepts incoming connections from clients and receives blocks of
memory pages from them. Whenever a new client is connected, the server waits for the client
to share memory and prints hex dumps of the memory blocks shared by the client. The client
is disconnected and told to quit once the hex dumps are performed. This server is capable of
Page 14 of 15
COMP3301 2024 Assignment 2
handling multiple clients at the same time.
a2server takes the following parameters:
a2server
where socket-path is the path to the socket to create. This path could be located anywhere
(or be /tmp/a2test_tmp if you are unsure), as long as you specify the same path when you
run the client(s).
a2client
This is a simple client which attempts to connect to a2server and shares a page of memory
once connected. It quits once the server replies back that it had received and hex dumped the
memory block(s) sent.
a2client takes the following parameters:
a2client
where socket-path is the path to the socket to connect to. This path must match the path
which you’ve specified to the server.