Skip to content

Instantly share code, notes, and snippets.

@gongzhitaao
Last active August 29, 2015 14:07
Show Gist options
  • Save gongzhitaao/483abc94880080f5ad69 to your computer and use it in GitHub Desktop.
Save gongzhitaao/483abc94880080f5ad69 to your computer and use it in GitHub Desktop.
COMP 2710 Lab2
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <iostream>
#include <fstream>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
namespace lab2 {
struct entry {
time_t ts;
std::string sender;
std::string reciever;
std::string content;
};
inline bool fexists(const char *fname)
{
struct stat buffer;
return (stat (fname, &buffer) == 0);
}
bool lockfile(const char *fname, int &fd, bool wait = false)
{
if (!fexists(fname)) {
fd = open(fname, O_CREAT, S_IRUSR | S_IWUSR);
close(fd);
}
if ((fd = open(fname, O_RDWR)) == -1) {
perror("open");
exit(1);
}
// l_type, l_whence, l_start, l_len
flock wl = {F_WRLCK, SEEK_SET, 0, 0};
if (wait)
return (-1 != fcntl(fd, F_SETLKW, &wl));
return (-1 != fcntl(fd, F_SETLK, &wl));
}
bool unlockfile(int fd)
{
// assume fd is valid
flock ul = {F_UNLCK, SEEK_SET, 0, 0};
return (-1 != fcntl(fd, F_SETLK, &ul));
}
inline std::string grpfname(const std::string &grp)
{
return "dat/Group/" + grp;
}
inline std::string usrfname(const std::string &usr)
{
return "dat/User/" + usr;
}
void gethist(int fd, std::vector<entry> &hist,
const std::string &sender = "",
const std::string &reciever = "")
{
int len = lseek(fd, 0, SEEK_END);
char *s = new char[len + 1];
memset(s, 0, len + 1);
lseek(fd, 0, SEEK_SET);
read(fd, s, len);
for (int a = 2, b; a < len; a = b + 2) {
// extract timestamp: integer
for (b = a + 1; s[b] != ')'; ++b)
;
s[b] = 0;
time_t ts = atoi(s + a);
// extract sender
a = b + 4;
for (b = a; !('>' == s[b] && '|' == s[b+1]); ++b)
;
s[b] = 0;
std::string name = s + a;
// extract content
a = b + 2;
for (b = a; b < len - 1 && !('[' == s[b] && '(' == s[b+1]);
++b)
;
std::string content;
if (b == len - 1)
content = s + a;
else {
s[b] = 0;
content = s + a;
}
if (sender.empty())
hist.push_back({ts, name, reciever, content});
else
hist.push_back({ts, sender, name, content});
}
delete s;
}
void reverse(int fd)
{
std::vector<entry> hist;
gethist(fd, hist);
sort(hist.begin(), hist.end(),
[](const entry &a, const entry &b) {
return difftime(a.ts, b.ts) > 0;
});
lseek(fd, 0, SEEK_SET);
for (auto it = hist.begin(); it != hist.end(); ++it) {
std::string tmp =
"[(" + std::to_string(it->ts) + ")]" + "|" + // timestamp
"<" + it->sender + ">" + "|" + // sender/reciever
it->content; // msg
write(fd, tmp.c_str(), tmp.size());
fsync(fd);
}
}
void msgrw(int ffd, const std::string &from,
const std::string &to)
{
/* read in the multiline message */
printf("\nEnter message: ");
std::string msg, line;
while (1) {
getline(std::cin, line);
size_t sz = line.size();
if (sz >= 2 &&
line[sz-1] == '$' &&
line[sz-2] == '$') {
msg += line.substr(0, sz - 2);
break;
}
msg += line + "\n";
}
// get current timestamp
time_t ts;
time(&ts);
/* Now we proceed to send the message. */
// senders
{
// construct msg: format: [(timestamp)]|Reciver|msg
std::string tmp =
"[(" + std::to_string(ts) + ")]" + "|" + // timestamp
"<" + to.substr(to.find_last_of('/') + 1) + ">" + "|" + // receiver
msg; // msg
lseek(ffd, 0, SEEK_END);
write(ffd, tmp.c_str(), tmp.size());
fsync(ffd);
reverse(ffd);
}
// receiver
{
// construct msg: format: [(timestamp)]|Sender|msg
std::string tmp =
"[(" + std::to_string(ts) + ")]" + "|" + // timestamp
"<" + from + ">" + "|" + // sender
msg; // msg
int fd;
if (!lockfile(to.c_str(), fd, true)) {
perror("fcntl");
exit(1);
}
lseek(fd, 0, SEEK_END);
write(fd, tmp.c_str(), tmp.size());
fsync(fd);
reverse(fd);
if (!unlockfile(fd)) {
perror("fcntl");
exit(1);
}
close(fd);
}
}
class DMS
{
public:
static DMS & inst() {
static DMS dms;
return dms;
}
~DMS() {
if (ufd_ != -1) {
std::ofstream of((usrfname(user_) + ".txt").c_str());
for (auto it = curgrp_.begin(); it != curgrp_.end(); ++it)
of << *it << std::endl;
of.close();
if (!unlockfile(ufd_)) {
perror("fcntl");
exit(1);
}
}
}
void run() {
printf("\n"
"============================================================\n"
"| Distributed Messaging System! |\n"
"============================================================\n\n");
login();
while (1) {
printf("\n"
"Broadcast (b), Multicast (m), Unicast(u), Wall (w), Home (h),\n"
"Create Group (g), Join Group (j), Quit (q)\n");
printf("\nEnter option: ");
int opt = getchar();
while (getchar() != '\n') ;
switch (opt) {
case 'b':
broadcast(); break;
case 'm':
multicast(); break;
case 'u':
unicast(); break;
case 'w':
wall(); break;
case 'h':
home(); break;
case 'g':
create(); break;
case 'j':
join(); break;
case 'q':
quit(); goto end;
default:
fprintf(stderr, "Unknown options\n");
}
}
end:
;
}
protected:
void login() {
while (1) {
printf("Please enter user name: ");
// Get username from input
getline(std::cin, user_);
// Empty username is invalid
if (user_.empty()) continue;
/* We allow one instance per user. If you logged in somewhere
else, then the file `dat/User/username` will be locked. In
that case you will have to login as another user.
*/
if (!lockfile(usrfname(user_ + ".out").c_str(), ufd_)) {
// perror("fcntl");
fprintf(stderr, "You've logged in somewhere else.\n");
continue;
}
break;
}
int fd;
// create `username` file
fd = open(usrfname(user_).c_str(), O_CREAT, S_IRUSR | S_IWUSR);
close(fd);
// create `username.txt` or read in from it if exists
const char *tmp = (usrfname(user_) + ".txt").c_str();
if (!fexists(tmp)) {
fd = open(tmp, O_CREAT, S_IRUSR | S_IWUSR);
close(fd);
} else {
std::ifstream fin(tmp);
std::string grp;
while (fin >> grp)
curgrp_.insert(grp);
}
printf("\n"
"============================================================\n"
"| Welcome to Distributed Messaging System, %-12s|\n"
"============================================================\n",
(user_ + "!").c_str());
}
void broadcast() {
msgrw(ufd_, user_, grpfname("All"));
}
void multicast() {
if (curgrp_.empty()) {
fprintf(stderr, "User is not in any group.\n");
return;
}
printf("\nEnter the group name: ");
std::string to;
getline(std::cin, to);
if (!to.empty() && curgrp_.find(to) != curgrp_.end())
msgrw(ufd_, user_, grpfname(to));
else
fprintf(stderr, "User is not in %s\n", to.c_str());
}
void unicast() {
printf("\nEnter the recipient user name: ");
std::string to;
while (to.empty())
getline(std::cin, to);
if (!fexists(usrfname(to).c_str())) {
fprintf(stderr, "No user named %s\n", to.c_str());
return;
}
msgrw(ufd_, user_, usrfname(to));
}
void wall() {
printf("\n"
"============================================================\n"
"|%25s’s Wall Page |\n"
"============================================================\n\n",
user_.c_str());
std::vector<entry> hist;
gethist(ufd_, hist, user_);
sort(hist.begin(), hist.end(),
[](const entry &a, const entry &b) {
return difftime(a.ts, b.ts) > 0;
});
for (size_t i = 0; i < 2 && i < hist.size(); ++i) {
printf("%s(%s)>>\n%s\n",
user_.c_str(), hist[i].reciever.c_str(),
hist[i].content.c_str());
}
if (hist.size() > 2) {
bool cont = false;
while (1) {
printf("\n More message? (yes/no): ");
std::string ans;
getline(std::cin, ans);
if (0 == ans.compare("yes")) {
cont = true;
break;
} else if (0 == ans.compare("no")) {
break;
}
}
if (cont) {
printf("\n");
for (size_t i = 2; i < hist.size(); ++i) {
printf("%s(%s)>>\n%s\n\n",
user_.c_str(), hist[i].reciever.c_str(),
hist[i].content.c_str());
}
}
}
printf("\n"
"============================================================\n"
"| End of %-35s|\n"
"============================================================\n\n",
(user_ + "'s Wall Page").c_str());
}
void home() {
printf("\n"
"============================================================\n"
"|%25s’s Home Page |\n"
"============================================================\n\n",
user_.c_str());
std::vector<std::string> flist = {
usrfname(user_), grpfname("All")
};
for (auto it = curgrp_.begin(); it != curgrp_.end(); ++it)
flist.push_back(grpfname(*it));
std::vector<entry> hist;
for (auto it = flist.begin(); it != flist.end(); ++it) {
int fd;
if (!lockfile(it->c_str(), fd, true)) {
perror("fcntl");
exit(1);
}
gethist(fd, hist, "", it->substr(it->find_last_of('/') + 1));
if (!unlockfile(fd)) {
perror("fcntl");
exit(1);
}
close(fd);
}
sort(hist.begin(), hist.end(),
[](const entry &a, const entry &b) {
return difftime(a.ts, b.ts) > 0;
});
for (size_t i = 0; i < 2 && i < hist.size(); ++i) {
if (!hist[i].reciever.compare(user_))
printf("%s>>\n%s\n",
hist[i].sender.c_str(),
hist[i].content.c_str());
else
printf("%s(%s)>>\n%s\n",
hist[i].sender.c_str(), hist[i].reciever.c_str(),
hist[i].content.c_str());
}
if (hist.size() > 2) {
bool cont = false;
while (1) {
printf("\n More message? (yes/no): ");
std::string ans;
getline(std::cin, ans);
if (0 == ans.compare("yes")) {
cont = true;
break;
} else if (0 == ans.compare("no")) {
break;
}
}
if (cont) {
printf("\n");
for (size_t i = 2; i < hist.size(); ++i) {
if (!hist[i].reciever.compare(user_))
printf("%s>>\n%s\n",
user_.c_str(),
hist[i].content.c_str());
else
printf("%s(%s)>>\n%s\n",
hist[i].sender.c_str(), hist[i].reciever.c_str(),
hist[i].content.c_str());
}
}
}
printf("\n"
"============================================================\n"
"| End of %-35s|\n"
"============================================================\n\n",
(user_ + "'s Wall Page").c_str());
}
void create() {
std::string gn;
while (1) {
printf("\nPlease enter the group name: ");
getline(std::cin, gn);
if (!gn.empty() && !fexists(grpfname(gn).c_str()))
break;
printf("Group name either empty or exists.\n");
}
int fd = open(grpfname(gn).c_str(), O_CREAT, S_IRUSR | S_IWUSR);
close(fd);
}
void join() {
std::string gn;
while (1) {
printf("\nPlease enter the group name: ");
getline(std::cin, gn);
if (gn.empty()) {
printf("\nEmpty group name");
continue;
}
if (!fexists(grpfname(gn).c_str())) {
fprintf(stderr, "\nNo group with name %s\n", gn.c_str());
continue;
}
if (curgrp_.find(gn) != curgrp_.end()) {
fprintf(stderr, "\nYou are already in the group.\n");
continue;
}
break;
}
curgrp_.emplace(gn);
}
void quit() {
printf("\n"
"============================================================\n"
"| Thank you for using the Distributed Messaging System |\n"
"============================================================\n\n");
}
private:
DMS()
: user_("")
, ufd_(-1) { }
DMS(const DMS &);
void operator=(const DMS &);
std::string user_;
std::set<std::string> curgrp_;
int ufd_;
};
}
int main()
{
lab2::DMS &dms = lab2::DMS::inst();
dms.run();
return 0;
}
CXX=g++
EXE=main
FLG=-std=c++11 -O2 -Wall
all : $(EXE)
$(EXE) : main.cpp
$(CXX) $(FLG) -o $@ $<
clean :
rm -rf $(EXE)
dat-clean :
rm -rf dat/Group/*
rm -rf dat/User/*
.PHONY : all clean
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment