[PATCH V2] hg_open: the function that opens the connection with cmdserver

Iulian Stana julian.stana at gmail.com
Wed Sep 18 15:19:47 CDT 2013


# HG changeset patch
# User Iulian Stana <julian.stana at gmail.com>
# Date 1379448402 -10800
#      Tue Sep 17 23:06:42 2013 +0300
# Node ID 18301c2cb3c8ae19837061232e862c591baaf907
# Parent  f461e172467707156321c20874fde6fb860e52d6
hg_open: the function that opens the connection with cmdserver

This commit will also contain the additional functions that comes with the
hg_open function.

The read_hello function, it's a private function that reads the cmdserver hello
message and verify it.

The hg_read_header function is the function that reads the channel and the
lenght for the next command server action (send data or wait to receive data).
This function will read the next header or will return the old header if there
is still data to read from the pipe.
While the next channel is read, the bytes_on_pipe field is initialize with the
lenght received from the pipe.
Note: if you read from pipe you must decrease the bytes_on_pipe field.

The swap_uint32 function will swap a number from his little endian form to his
big endian form or vice versa. (The command server it's sending the lenght in a
big endian form)

The hg_open function will open a server and will establish a connection with it
through a bidimensional pipe.

diff --git a/hglib/client.c b/hglib/client.c
new file mode 100644
--- /dev/null
+++ b/hglib/client.c
@@ -0,0 +1,171 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "client.h"
+#include "utils.h"
+
+#define HGPATH "hg"
+
+/*
+ * The function will read the header from the command server and will
+ * save it to handle pointer
+ * */
+hg_header *hg_read_header(hg_handle *handle)
+{
+	uint32_t length;
+	char ch_char;
+
+	if (!handle){
+		errno = EINVAL;
+		return NULL;
+	}
+
+	if (handle->bytes_on_pipe == 0){
+		if (read(handle->p_read, &ch_char, 1) < 0){
+			return NULL;
+		}
+		if (read(handle->p_read, &length, sizeof(uint32_t)) < 0){
+			return NULL;
+		}
+		handle->header->length = swap_uint32(length);
+		handle->bytes_on_pipe = handle->header->length;
+		switch(ch_char){
+			case 'o':
+				handle->header->channel = o;
+				break;
+			case 'e':
+				handle->header->channel = e;
+				break;
+			case 'r':
+				handle->header->channel = r;
+				break;
+			case 'I':
+				handle->header->channel = I;
+				break;
+			case 'L':
+				handle->header->channel = L;
+				break;
+			default:
+				handle->header->channel = wrong_channel;
+				break;
+		}
+	}
+	return handle->header;
+}
+
+/**
+ * \brief Reading the hello msg from cmd server.
+ *
+ * After the connection, the command server sends a hello message.
+ * The message contains the command server capabilities and the messages
+ * encoding.
+ * \param handle The handle of the connection, wherewith I want to communicate
+ * \retval  0 if succesfull
+ * \retval -1 to indicate an error, with errno set appropriately
+ *
+ * errno can be:
+ *       - EINVAL  - Invalid argument (handle it's set to a null pointer)
+ *       - read(2) command errors
+ * */
+int read_hello(hg_handle *handle)
+{
+	char *buffer;
+	hg_header *ch;
+	int nc;
+
+	if (!handle) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	ch = hg_read_header(handle);
+
+	if (ch->length == 0){
+		return -1;
+	}
+
+	buffer = malloc(ch->length + 1);
+
+	if (nc = read(handle->p_read, buffer, ch->length), nc < 0){
+		free(buffer);
+		return -1;
+	}
+	buffer[ch->length] = '\0';
+	handle->bytes_on_pipe -= nc;
+
+	free(buffer);
+
+	return 0;
+}
+
+/*
+ * Open the connection with the mercurial command server.
+ * */
+hg_handle *hg_open(const char *path, char *encoding)
+{
+	hg_handle *handle = malloc(sizeof(hg_handle));
+	handle->header = malloc(sizeof(hg_header));
+	/* len("%s serve --cmdserver pipe --config ui.interactive=True") = 52 */
+	char *command = malloc(strlen(HGPATH) + 53);
+	int wpipe[2];
+	int rpipe[2];
+	int c_write;
+	int c_read;
+	pid_t childpid;
+
+	sprintf(command,
+		"%s serve --cmdserver pipe --config ui.interactive=True",
+		HGPATH);
+
+	if (path){
+		command = realloc(command, strlen(command) + strlen(path) + 5);
+		sprintf(command, "%s -R %s", command, path);
+	}
+
+	if (pipe(wpipe) < 0 || pipe(rpipe) < 0) {
+		return NULL;
+	}
+	handle->p_read = rpipe[0];
+	c_write = rpipe[1];
+	c_read = wpipe[0];
+	handle->p_write = wpipe[1];
+	handle->protect = 0;
+	handle->bytes_on_pipe = 0;
+
+	if ((childpid = fork()) < 0) {
+		return NULL;
+
+	} else if (childpid == 0) {/* child */
+		close(handle->p_write);
+		close(handle->p_read);
+		if (dup2(c_read, STDIN_FILENO) < 0){
+			return NULL;
+		}
+		close(c_read);
+		if (dup2(c_write, STDOUT_FILENO) < 0){
+			return NULL;
+		}
+		close(c_write);
+		if (execl("/bin/sh", "sh", "-c", command, NULL) < 0){
+			return NULL;
+		}
+	} else {/* parent */
+		close(c_read);
+		close(c_write);
+	}
+
+	free(command);
+
+	if (read_hello(handle) < 0)
+		return NULL;
+
+	handle->out_data = NULL;
+	handle->out_data_size = 0;
+	return handle;
+}
+
diff --git a/hglib/client.h b/hglib/client.h
--- a/hglib/client.h
+++ b/hglib/client.h
@@ -1,4 +1,4 @@
-ifndef _CLIENT_H_
+#ifndef _CLIENT_H_
 #define _CLIENT_H_
 
 #include <errno.h>
diff --git a/hglib/utils.c b/hglib/utils.c
new file mode 100644
--- /dev/null
+++ b/hglib/utils.c
@@ -0,0 +1,17 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "utils.h"
+
+/*
+ * Byte swap unsigned int
+ * */
+uint32_t swap_uint32(uint32_t val)
+{
+	val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF);
+	return (val << 16) | (val >> 16);
+}
+
diff --git a/hglib/utils.h b/hglib/utils.h
new file mode 100644
--- /dev/null
+++ b/hglib/utils.h
@@ -0,0 +1,13 @@
+#ifndef _UTILS_CHG_H_
+#define _UTILS_CHG_H_
+
+#include <stdint.h>
+
+/** 
+ * \brief Byte swap unsigned int
+ * \param val an uint32_t integer
+ * \retval uint32_t the bigendian form of val value
+ * */
+uint32_t swap_uint32(uint32_t val);
+
+#endif


More information about the Mercurial-devel mailing list