when create a new page at mediawiki, the following sql-statements will be created.

(从MySQL的log分析得出,不保证Mediawiki本身就一定按照此顺序执行,

其中也省略了一些临时的步骤,红色表示关键步骤,可以保证mediawiki的该page的正常查看以及查找)

1. insert into page。(page_latest = 0,len = 0 at this insert)

2.insert into text。

3.insert into revision。

(以上三个表有外键关联,

page.page_id = revision.rev_page ,

page.page_latest = revision.rev_id,

revsion.rev_text_id = text.old_id

)

4. update page set page_lastest, len.

5. insert logging.

6. update user set user_editcount = user_editcount + 1

7. update user set user_touch = nowdate()

8. insert into objectcache( this table is used for a few generic cache operations if not using Memcached)

9. update site_stats set ss_total_edit and ss_total_page ( add 1)

10. replace into searchindex.

根据RFC 959实现的一个简易版本,用户名和密码都只是写在代码中,也只提供了PORT模式而没有PASV模式。实现了基本的登录,上传下载以及目录的变更(用leapftp,浏览器的ftp://均测试过),后续会继续写一些其他功能,尽量做成一个像样的ftpserver,但新日志只贴新函数就好,不想把一个日志搞太大。


#include "unpheader.h"
char *env_init[] ={"LC_ALL=en_US",NULL};

struct ftpuser{
 char dataip[20];
 int dataport;
 int loginok;
 int userok;
};

static int listenport=8021;
void init_ftpuser(struct ftpuser *myftp)
{
 memset(myftp->dataip,'�',20);
 myftp->dataport = 0;
 myftp->loginok = 0;
 myftp->userok = 0;
}

void do_stor(int connfd,char *storfile,struct ftpuser *myftp)
{
 char *storstart = "150 Opening data connection.rn";
 char *storend = "226 Transfer completed.rn";
 umask(S_IWGRP | S_IWOTH);
 int filefd = open(storfile,O_RDWR | O_CREAT,RWRWRW);
 write(connfd,storstart,strlen(storstart));
 int datasockfd = getconnection(myftp->dataip,myftp->dataport);
 char buf[MAXLINE];
 int n=0;
 while((n = read(datasockfd,buf,MAXLINE)) > 0)
 {
   buf[n]='�';
   write(filefd,buf,n);
 }
 close(datasockfd);
 write(connfd,storend,strlen(storend));
 return;
}

void do_retr(int connfd,char *retrfile,struct ftpuser *myftp)
{
 char *retrstart = "150 Opening data connection.rn";
 char *retrend = "226 Transfer completed.rn";
 char *retrnofile = "503 no such file.rn"; 
 int filefd;
 if( (filefd = open(retrfile,O_RDONLY) ) != -1)
 {
  write(connfd,retrstart,strlen(retrstart));
  int datasockfd = getconnection(myftp->dataip,myftp->dataport);
  char buf[MAXLINE];
  int n=0;
  while((n = read(filefd,buf,MAXLINE)) > 0)
  {
   buf[n]='�';
   write(datasockfd,buf,n);
  }
  close(datasockfd);
  write(connfd,retrend,strlen(retrend));
 }
 else
  write(connfd,retrnofile,strlen(retrnofile));
 return;
}

void do_mode(int connfd,char *modetype)
{
 char *modeok = "200 stream mode is okrn";
 char *modefail = "500 only stream mode is supportrn";
 if(strncmp(modetype,"S",1) == 0)
  write(connfd,modeok,strlen(modeok));
 else
  write(connfd,modefail,strlen(modefail));
 return;
}

void do_type(int connfd,char *type)
{
 char *settypeI = "200 type set to I.rn";
 char *settypeA = "200 type set to A.rn";
 char *seterror = "500 parm error.rn";
 if(strncasecmp(type,"I",1) == 0)
  write(connfd,settypeI,strlen(settypeI));
 else if(strncasecmp(type,"A",1) == 0)
  write(connfd,settypeA,strlen(settypeA));
 else
  write(connfd,seterror,strlen(seterror));
 return ;
}

void do_pwd(int connfd,struct ftpuser *myftp)
{
 char output[MAXLINE];
 memset(output,'�',MAXLINE);
 char currdir[MAXLINE];
 memset(currdir,'�',MAXLINE);
 getcwd(currdir,MAXLINE);
 snprintf(output,MAXLINE,"257 "%s"rn",currdir);
 write(connfd,output,strlen(output));
 return;
}

void do_noop(int connfd)
{
 write(connfd,"200 NOOP ok.rn",14);
 return;
}

void do_cwd(int connfd,char *postfix,char *rootdir)
{
 char *cwdok = "250 Directory successfully changed.rn";
 char *cwderror = "500 Fail to change directory.rn";
 char currdir[MAXLINE];
 memset(currdir,'�',MAXLINE);
 getcwd(currdir,MAXLINE);
 if( strcmp(rootdir,currdir) == 0 && strcmp(postfix,"..") == 0)
  write(connfd,cwderror,strlen(cwderror));
 else if(chdir(postfix) >= 0)
  write(connfd,cwdok,strlen(cwdok));
 else
  write(connfd,cwderror,strlen(cwderror));
 return ;
}

void do_quit(int connfd)
{
 char *quitok="221 Goodbye.rn";
 write(connfd,quitok,strlen(quitok));
 return ;
}

void do_user(int connfd,char *username,struct ftpuser* myftp)
{
 char *nameok="331 please specify the passwordrn";
 char *nameerror = "430 username errorrn";
 char buf[MAXLINE];
 if(strncmp(username,"yourname",6) == 0)
 {
  write(connfd,nameok,strlen(nameok));
  myftp->userok = 1;
 }
 else
  write(connfd,nameerror,strlen(nameerror));
 return;
}

void do_pass(int connfd,char *userpass,struct ftpuser* myftp)
{
 char *passerror = "430 password errorrn";
 char *loginok = "230 Login Successfulrn";
 char *nouser = "530 No User Specifiedrn";
 if(strncmp(userpass,"yourpass",8) == 0)
 {
  if(myftp->userok == 1)
  {
   write(connfd,loginok,strlen(loginok));
   myftp->loginok = 1;
  }
  else
   write(connfd,nouser,strlen(nouser));
 }
 else
  write(connfd,passerror,strlen(passerror));
 return;
}

void do_pasv(int connfd)
{
 char *notsupport="500 passive mode not supported.rn";
 write(connfd,notsupport,strlen(notsupport));
 return;
}

void do_list(int connfd,struct ftpuser* myftp)
{
 char *noport = "425 User Port Firstrn";
 char *opening = "150 Here comes the directory listing.rn";
 char *transferok = "226 Transfer complete.rn";
 if( myftp->dataport != 0)
 {
  write(connfd,opening,strlen(opening));
  pid_t pid;
  if((pid = fork()) == 0)
  {
   close(connfd);
   int datasockfd = getconnection(myftp->dataip,myftp->dataport); 
   dup2(datasockfd,1);
   close(datasockfd);
   execle("/bin/ls","ls","-lan","--time-style=+%b %d %H:%M",(char *)0,env_init);
  }
  else
  {
   waitpid(pid,NULL,0);
   write(connfd,transferok,strlen(transferok));  
  }
 }
 else
 {
  write(connfd,noport,strlen(noport));
 }
}

void do_port(int connfd,char *ipport,struct ftpuser* myftp)
{
 char *portok="200 PORT Command successful.rn";
 char temp[MAXLINE];
 strncpy(temp,ipport,strlen(ipport));
 memset(myftp->dataip,'�',20);
 strcat(myftp->dataip,strtok(temp,","));
 int i=0;
 while(i<3)
 {
  strcat(myftp->dataip,".");
  strcat(myftp->dataip,strtok(NULL,","));
  i++;
 }
 myftp->dataport = atoi(strtok(NULL,",")) * 256 ;
 myftp->dataport = myftp->dataport + atoi(strtok(NULL,","));
 write(connfd,portok,strlen(portok));
 return;
}

void handler(int connfd,char* filedir)
{
 int n;
 char command[MAXLINE];
 char rootdir[MAXLINE];
 memset(rootdir,'�',MAXLINE);
 getcwd(rootdir,MAXLINE);
 struct ftpuser myftp;
 init_ftpuser(&myftp);
 char *welcome="220 welcome to myFTP!rn";
 write(connfd,welcome,strlen(welcome));
 while(( n = read(connfd,command,MAXLINE))>0)
 {
  errno = 0;
  if( command[n-2] == 'r')
   command[n-2] = '�';
  if( command[n-1]== 'n')
   command[n-1] = '�';
  printf("get command: %sn",command);
  if( strncmp(command,"USER ",5) == 0)
   do_user(connfd,command + 5,&myftp);
  else if( strncmp(command,"PASS ",5) == 0)
   do_pass(connfd,command + 5,&myftp);
  else if(strncmp(command,"LIST",4) == 0) //list file
   do_list(connfd,&myftp);
  else if(strncmp(command,"CWD ",4) == 0)  //change dir
   do_cwd(connfd,command + 4,rootdir);
  else if(strncmp(command,"PWD",3) == 0)
   do_pwd(connfd,&myftp);
  else if(strncmp(command,"PORT ",5) == 0)
   do_port(connfd,command + 5,&myftp);
  else if(strncmp(command,"PASV",4) == 0)
   do_pasv(connfd);
  else if(strncmp(command,"MODE ",5) == 0)
   do_mode(connfd,command + 5);
  else if(strncmp(command,"TYPE ",5) == 0)
   do_type(connfd,command + 5);
  else if(strncmp(command,"RETR ",5) == 0)
   do_retr(connfd,command + 5,&myftp);
  else if(strncmp(command,"STOR ",5) == 0)
   do_stor(connfd,command + 5,&myftp);
  else if(strncmp(command,"CDUP",4) == 0)  //CDUP
   do_cwd(connfd,"..",rootdir);
  else if(strncmp(command,"QUIT",4) == 0)
  {
   do_quit(connfd);
   break;
  }
  else
   write(connfd,"500 unknown command.rn",22);
 }
 close(connfd);
 return;
}

int main(int argc,char *argv[])
{
 int listenfd,connfd;
 struct sockaddr_in cliaddr;
 socklen_t clilen;
 pid_t handlerpid;
 listenfd = initlisten(listenport);
 chdir(argv[1]);
 printf("server has started,listen port is %dn",listenport);
 while(1)
 {
  if((connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&clilen)) == -1)
  {
   printf("accept error! %s",strerror(errno));
   return errno;
  }
  printf("get a connectionn");
  if((handlerpid = fork()) == 0)
  {
   handler(connfd,argv[1]);
   exit(0);
  }
  close(connfd);
 }
 return 0;
}

以后贴的程序可能会一直用这个东西,所以先贴上来,就三个函数,不自动重启被信号中断的系统调用的signal_intr,建立连接sockfd的getconnection(ip,port)和建立倾听sockfd的initlisten(port)。


#ifndef _UNP_H
#define _UNP_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <fcntl.h>
#define MAXLINE 1024
#define RWRWRW (S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP | S_IWOTH | S_IROTH)
#define RWXRWXRWX (S_IWUSR | S_IRUSR | S_IXUSR | S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IROTH | S_IXOTH)
typedef void (*sighandler_t)(int);

sighandler_t signal_intr(int signo,sighandler_t func)
{
 struct sigaction act,oact;
 act.sa_handler = func;
 sigemptyset(&act.sa_mask);
 act.sa_flags = 0;
#ifdef SA_INTERRUPT
 act.sa_flags |= SA_INTERRUPT;
#endif
 if(sigaction(signo,&act,&oact) < 0)
  return (SIG_ERR);
 return (oact.sa_handler);
}

int initlisten(int port)
{
 int listenfd;
 struct sockaddr_in servaddr;
 if( (listenfd = socket(AF_INET,SOCK_STREAM,0))< 0)
 {
  printf("socket error! %s\n",strerror(errno));
  return -1;
 }
 bzero(&servaddr,sizeof(struct sockaddr_in));
 servaddr.sin_family = AF_INET;
 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 servaddr.sin_port = htons(port);
 if( -1 == (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))) )
 {
  printf("bind error! %s,%d\n",strerror(errno),port);
  return -1;
 }
 if( -1 == (listen(listenfd,1024)))
 {
  printf("listen error! %s,%d\n",strerror(errno),port);
  return -1;
 }
 return listenfd;
}

int getconnection(char *ip,int port)
{
 int sockfd,reuse=1;
 struct sockaddr_in cliaddr;
 if( (sockfd = socket(AF_INET,SOCK_STREAM,0))< 0)
 {
  printf("socket error! %s\n",strerror(errno));
  return -1;
 }
 if( setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(int)) < 0)
 {
  printf("set reuseaddr error! %s\n",strerror(errno));
  return -1;
 }
 bzero(&cliaddr,sizeof(struct sockaddr_in));
 cliaddr.sin_family = AF_INET;
 cliaddr.sin_port = htons(port);
 if ( inet_pton(AF_INET,ip,&cliaddr.sin_addr) <= 0)
 {
  printf("inet_pton error for %s,%s\n",ip,strerror(errno));
  return -1;
 }

 if(connect(sockfd,(struct sockaddr*)&cliaddr,sizeof(cliaddr)) < 0)
 {
  printf("connect error for %s\n",strerror(errno));
  return -1;
 }

 return sockfd;
}

#endif

常见的linux对read,write等系统调用被信号中断会自动设置重启,通过sigaction设置不自动重启。


typedef void (*sighandler_t)(int);

sighandler_t signal_intr(int signo,sighandler_t func)
{
 struct sigaction act,oact;
 act.sa_handler = func;
 sigemptyset(&act.sa_mask);
 act.sa_flags = 0;
#ifdef SA_INTERRUPT
 act.sa_flags |= SA_INTERRUPT;
#endif
 if(sigaction(signo,&act,&oact) < 0)
  return (SIG_ERR);
 return (oact.sa_handler);
}

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#define RWRWRW (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)

int main(void)
{
 pid_t pid;
 if((pid = fork()) == 0)
 {
  umask( S_IWGRP | S_IWOTH );
  int fd = open("testls",O_RDWR | O_CREAT,RWRWRW);
  dup2(fd,1);
  close(fd);
  execl("/bin/ls","ls",(char *)0);
 }
 else
 {
  waitpid(pid);
  return 0;
 }
}

大三时候的系统结构实验,为了实现多进程的同步,由于变量都是进程的单独拷贝,所以超级傻的用了一个文件做标志,不同的进程对同一文件进行读写不同的数做锁。

其实POSIX_THREAD_PROCESS_SHARED支持就可以直接利用pthread_mutex_t进行进程间上锁

#include "unpheader.h"
#include <pthread.h>
#include <sys/mman.h>

static pthread_mutex_t *mptr;

void my_lock_init(void)               //锁初始化,PTHREAD_PROCESS_SHARED属性
{
int    fd;
pthread_mutexattr_t    mattr;

fd=open("/dev/zero",O_RDWR,0);
mptr = mmap(0,sizeof(pthread_mutex_t),PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);

close(fd);

pthread_mutexattr_init(&mattr);
pthread_mutexattr_setpshared(&mattr,PTHREAD_PROCESS_SHARED);
pthread_mutex_init(mptr,&mattr);
}

int main(void)
{
pid_t pid;
my_lock_init();
if((pid = fork()) == 0)            //child process
{
int i=0;
for(i=0;i<10;i++)
{
pthread_mutex_lock(mptr);
printf("child get mutex!\n");
sleep(1);
pthread_mutex_unlock(mptr);
sleep(1);
}
}
else                                    //father process
{
int i=0;
for(i=0;i<10;i++)
{
pthread_mutex_lock(mptr);
printf("father get mutex!\n");
sleep(1);
pthread_mutex_unlock(mptr);
sleep(1);
}
}
return 0;
}

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <alloca.h>

#define MAXLINE 1024

struct addrinfo *host_serv(const char *hostname,const char *service,int family,int sockettype)
{
  int n;
  struct addrinfo  hints,*res;

  bzero(&hints,sizeof(struct addrinfo));
  hints.ai_flags = AI_CANONNAME;
  hints.ai_family = family;
  hints.ai_socktype = sockettype;

  if((n = getaddrinfo(hostname,service,&hints,&res))!=0)
   return NULL;
 
 return res;
}

int tcp_connect(const char* host,const char *serv)
{
 int sockfd,n;
 struct addrinfo *res,*ressave;
 if((res=host_serv(host,serv,AF_UNSPEC,SOCK_STREAM)) == NULL)
 {
  printf("tcp_connect error for %s,%s:%s\n",host,serv,strerror(errno));
  exit(-1);
 }
 ressave=res;
 while(res != NULL)
 {
  sockfd = socket(res->ai_family,res->ai_socktype,res->ai_protocol);

  if(sockfd < 0)
   continue;
  if(connect(sockfd,res->ai_addr,res->ai_addrlen) == 0)
   break;

  close(sockfd);
  res = res -> ai_next;
 }
 if(res == NULL)
 {
  printf("tcp_connect error for %s,%s",host,serv);
  exit(-1);
 }
 freeaddrinfo(ressave);
 return sockfd;
}

int read_html(const char *host,const char *file,int fd)
{
 int sockfd,n;
 char recvline[256];
 char line[256];
 sockfd = tcp_connect(host,"80");
 int m=0;
 if(file != NULL)     
  m=snprintf(line,sizeof(line),"GET /%s HTTP/1.0\r\n\r\n",file);
 else                   //if there is no file specfied, the index.html will be got.
  m=snprintf(line,sizeof(line),"GET / HTTP/1.0\r\n\r\n");
 printf("%s\n",line);
 write(sockfd,line,m);
 while((n=read(sockfd,recvline,255)) >0)
 {
  recvline[n]=0;
  write(fd,recvline,sizeof(recvline));
 }
 close(sockfd);
 return 0;
}

void *printaddr_ip(struct addrinfo *test)
{
 if(test == NULL)
 {
  printf("the input parameter addrinfo is NULL");
  return;
 }
 while(test != NULL)
 {
  if( test -> ai_family == AF_INET)
  {
   struct sockaddr_in *sin = (struct sockaddr_in *) test-> ai_addr;
   printf("canonname:%s  IP:%s\n",test->ai_canonname,inet_ntoa( sin -> sin_addr));
   test = test -> ai_next;
  }
 }
}

int getlength(const char* str)
{
 const char *start = str;
 int i=0;
 while(*str != '\0')
 {
  i++;
  str++;
 }
 str = start;
 return i;

}

char *gethost(const char* hostfile,char *result,char *file)
{
 if( result == NULL)
  return NULL;
 const char *start = hostfile;
 const char *protocol[]={"http://","ftp://"};  //example protocol prefix
 
 int i=0;
 
 for(i=0;i< (sizeof(protocol)/sizeof(char *));i++)
 {
  if(strncmp(hostfile,protocol[i],sizeof(protocol[i])) == 0)
  {
   break;
  }
 }

 if( i != (sizeof(protocol)/sizeof(char *)) )   //find procotol match
 {
   hostfile = hostfile + getlength(protocol[i]) ;
 }

 strcpy(result,hostfile);
 char *end = strstr(result,"/");
 if( end != NULL)        //find file seperate
 {
  if( file != NULL)
   strcpy(file,end+1);
  *end = '\0';
 }
 else
  file[0]='\0';

 hostfile = start;
 return result;
}
int main(int argc,char *argv[])
{
 if(argc != 2 )
 {
  printf("usage: ./gethtml htmlpath\n");
  return -1;
 }
 
 char result[MAXLINE],file[MAXLINE];
 gethost(argv[1],result,file);
  read_html(result,file,STDOUT_FILENO);
 return 0;
}

参考unp第11章的 hostserv程序

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>

struct addrinfo *host_serv(const char *hostname,const char *service,int family,int sockettype)
{
  int n;
  struct addrinfo  hints,*res;

  bzero(&hints,sizeof(struct addrinfo));
  hints.ai_flags = AI_CANONNAME;
  hints.ai_family = family;
  hints.ai_socktype = sockettype;

  if((n = getaddrinfo(hostname,service,&hints,&res))!=0)
   return NULL;
 
 return res;
}

int main(int argc,char *argv[])
{
 if(argc != 2)
 {
  printf("usage: hostserv hostname\n");
  return -1;
 }
 struct addrinfo *test;
 test = host_serv(argv[1],NULL,AF_INET,SOCK_STREAM);
 while(test != NULL)
 {
  if( test -> ai_family == AF_INET)
  {
   struct sockaddr_in *sin = (struct sockaddr_in *) test-> ai_addr;
   printf("%s\n", inet_ntoa( sin -> sin_addr));
   test = test -> ai_next;
  }
 }
 return 0;
}

unix域协议是在单个主机上执行客户服务器通信方法,其实就是IPC的一种,但是程序的编写方式与TCP很类似,但比TCP套接口快出一倍的速度。

unix域套接口需要注意的是在UDP的unix套接口send之前需要显示的bind到一个pathname,这样服务器才有回答的路径。

unix域套接口传递描述符字采用sendmsg和recvmsg这两个函数的辅助数据msghdr进行传递,值得注意的是,在一方已经sendmsg的描述符字但是另外一端没有recvmsg之前(in flight),即使发送端关闭描述符字,接收方收到的描述符也是打开的。同时传递的是描述符字而不是一个字号,因此在接收进程中的描述符字号可能不同于发送进程。

socketpair是一个很有用的函数,很方便的创建了一个全双工的unix域套接口。

守护进程是UNIX系统中非常重要的一种进程,简单来说就是在后台运行且不与任何终端交互的进程,此种程序的的登记消息方式采用syslog函数由syslogd进程来执行。

让一个进程成为守护进程的方法为:

fork生成子进程,_exit父进程,对子进程调用setsid使得子进程成为一个新会话的会话首进程,接着屏蔽SIGHUP信号(会话首进程结束时会话组中所有进程都会收到),再次fork,依旧结束父进程,这样最后的孙子进程就不是会话首进程,然后关闭所有的描述字,并将0,1,2三个描述字都打开到/dev/null,最后openlog促使消息登记(但如果不采用LOG_NDELAY选项则unix域套接口一直到首次调用syslog才打开)。

linux下提供的将进程转为守护进程的函数为daemon, 可参考man手册。

inetd是一种常用的守护进程,它可以将很多需要服务的程序由自己一个人处理,根据/etc/inetd.conf和/etc/service的配置,inetd创建FTP,TELNET等各种等待端口,当有客户连接时根据/etc/inetd.conf的参数创建子进程或者直接调用程序服务。