How to interpret C function pointer syntax which borders on the arcane. e.g. "typedef void (*sighandler_t)(int);" ?
This is the general syntax for a function pointer is always
return_type (* pointer_name) ( variable1_type variable1_name , variable2_type variable2_name,....)
See more explanation and example at:
http://www.cprogrammingreference.com/Tutorials/Basic_Tutorials/Pointers.php#10
In my first example with a typedef, that is defining a function pointer as a type. Ignore the typedef and it is a function "sighandler_t", returns void and accepts one argument of type int. The (* ... ) groups the pointer asterisk with the typedef's name and indicates that the typdef is for a function pointer. e.g. "void sighandler_t(int);".
See more discussion at Stack Overflow: Reading function pointer syntax
Sunday, October 31, 2010
C Socket Server example, with semaphores, signal handling
This is a quick program that combines various examples from the GNU/Linux Application Programming book I have been studying.
The program is a BSD Socket based server (ch. 13), that accepts connections on a specific port and sends over TCP a "temperature" value, which at the moment I am simply randomly generating for example. I have added the use of counting semaphores (ch 17) to demonstrate a critical section of code as well as used POSIX signal handling (ch. 14) as a mechanism to cleanly exit. I developed this in Eclipse CDT IDE. I am not a C expert, this is my first C program since approx 2002 but it runs and compiles without warning!
Therm- ServerProject set up and dependencies
I created two static libraries for generating the random number/temperature reading. This are linked in my Eclipse project. For the purposes of this example you only need to know about these function prototypes (from thermapi.h)
Other than this, I depend on various standard libraries in Linux for sockets, signal handling, etc..
thermserv.c - Includes and Defines.
Print Server Info utility method just to clean up the main function a little. It still is too long.
Note: I was getting warnings from gcc for this function.
To resolve these, I added thermserv.h:
Warnings resolved!
back to the rest of thermserv.c:
main() - set up code
Now the block with all the hot action, an infinite while loop for the socket server to accept connections, get the current temperature value and then write to the socket. If a SIGUSR1 or SIGINT (ctrl+c) is received by the handlers the loop value loopCounter will be set to false and the while loop will end allowing for some clean up at the end to close the sockets, semaphores, etc...
The clean up code is reached by the signal handler function changing the loopCounter to allow the while loop to end. Other signals could cause the process to exit, I am not sure what that really means for the still open socket, and more importantly the semaphore. Might there be a better facility in C to handle this case?
Building therm-server
Invoking: GCC C Compiler - note libtherm and libexp are my custom static libraries
Invoking: GCC C Linker
Running therm-server
For the fun part. Open a terminal and navigate to /home/ammianus/workspace/libtherm/Debug
This launches the server, it acquires the semaphore and inits the socket listening on port 6777
To connect to the socket with a client, use telnet or a web browser and connect to 0.0.0.0:6777
Getting 72.720497 is the "temperature" that was our goal from the start!
On the server side it logged this connection attempt:
Etc..
The server will continue to run and accept connections, (try connecting with a browser and holding down 'F5').
To shutdown the server, you can send a signal to its PID
And server handles this signal gracefully
References
http://www.suite101.com/content/c-header-files-a2936
http://stackoverflow.com/questions/2406986/c-warning-implicit-declaration-of-function-exit
http://stackoverflow.com/questions/4009090/what-is-correct-way-to-have-single-signal-handler-function-for-multiple-signals
http://www.cs.odu.edu/~cs476/fall03/lectures/sockets.htm
Jones, M. Tim. GNULinux application programming. 2nd ed. Cengage Learning, 2005. Print.
Richard, W., and Stephen A. Advanced Programming in the UNIX Environment. Addison-Wesley Professional, 2008. Print.
The program is a BSD Socket based server (ch. 13), that accepts connections on a specific port and sends over TCP a "temperature" value, which at the moment I am simply randomly generating for example. I have added the use of counting semaphores (ch 17) to demonstrate a critical section of code as well as used POSIX signal handling (ch. 14) as a mechanism to cleanly exit. I developed this in Eclipse CDT IDE. I am not a C expert, this is my first C program since approx 2002 but it runs and compiles without warning!
Therm- ServerProject set up and dependencies
I created two static libraries for generating the random number/temperature reading. This are linked in my Eclipse project. For the purposes of this example you only need to know about these function prototypes (from thermapi.h)
/*
* Initialization for Therm API. Call before using any other function
*/
extern void initTherm( void );
/*
* Returns the current temperature reading from sensor
*/
extern float getTemp( void );
Other than this, I depend on various standard libraries in Linux for sockets, signal handling, etc..
thermserv.c - Includes and Defines.
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
/* sockets and addresses */
#include <sys.h>
#include <arpa.h>
#include <netinet.h>
#include <netinet.h>
#include <netinet.h>
#include <netinet.h>
#include <netdb.h>
/* semaphores */
#include <sys.h>
/* signal handling */
#include <sys.h>
#include <sys.h>
#include <signal.h>
/* my own includes for custom functions */
#include <thermapi.h>
#include "thermserv.h"
/* magic number constants */
#define MAX_BUFFER 128
#define THERM_SERVER_PORT 6777
#define THERM_SEM_ID 6778
static int loopCounter = 1;
/* Exit handler function called by sigaction */
void exitHandler(int sig, siginfo_t *siginfo, void *ignore) {
printf("*** Got %d signal from %d\n", siginfo->si_signo, siginfo->si_pid);
loopCounter = 0;
return;
}
Print Server Info utility method just to clean up the main function a little. It still is too long.
/*
* Prints out basic server information for startup
*/
void printTServerInfo(struct sockaddr_in servaddr, int cliLen,
time_t startupTime) {
printf("*** Thermo Server initialized.\n");
printf("*** cliLen size: %d\n", cliLen);
printf("*** Server Listening as %s:%d\n", inet_ntoa(servaddr.sin_addr),
ntohs(servaddr.sin_port));
printf("*** Server PID: %d\n", getpid());
printf("*** Start up time: %s", ctime(&startupTime));
}
Note: I was getting warnings from gcc for this function.
../thermserv.c: In function ‘main’:
../thermserv.c:122: warning: implicit declaration of function ‘printTServerInfo’
../thermserv.c: At top level:
../thermserv.c:180: warning: conflicting types for ‘printTServerInfo’
../thermserv.c:122: note: previous implicit declaration of ‘printTServerInfo’ was here
To resolve these, I added thermserv.h:
#ifndef THERMSERV_H_
#define THERMSERV_H_
/*
* Prints out basic server information for startup
*/
void printTServerInfo(struct sockaddr_in servaddr,
int cliLen,
time_t startupTime);
#endif /* THERMSERV_H_ */
Warnings resolved!
back to the rest of thermserv.c:
main() - set up code
/* start main */
int main(void) {
/*
* TODO implement server as daemon, per best
* practices, Advanced Programming in Unix Environment (ch 13, p417)
*/
int serverFd, connectionFd;
struct sockaddr_in servaddr;
char tempBuffer[MAX_BUFFER + 1];
time_t currentTime;
time_t startupTime;
float fread;
socklen_t cliLen;
struct sockaddr_in cliaAddr;
int therm_sem_id;
struct sembuf semAcquireBuffer;
struct sembuf semReleaseBuffer;
struct sigaction act;
/*init socket*/
serverFd = socket(AF_INET, SOCK_STREAM, 0);
/* set up semaphore */
therm_sem_id = semget(THERM_SEM_ID, 1, 0666 | IPC_CREAT);
if (therm_sem_id >= 0) {
printf("*** Semaphore %d\n", therm_sem_id);
/* init semaphore count to 1 */
semctl(therm_sem_id, 0, SETVAL, 1);
} else {
printf("Could not create/get semaphore with errno: %d %s", errno,
strerror(errno));
exit(-1);
}
semAcquireBuffer.sem_flg = 0;
semAcquireBuffer.sem_op = -1;
semAcquireBuffer.sem_num = 0;
semReleaseBuffer.sem_flg = 0;
semReleaseBuffer.sem_op = 1;
semReleaseBuffer.sem_num = 0;
/* Set exit handler function for SIGUSR1 , SIGINT (ctrl+c) */
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = exitHandler;
sigaction(SIGUSR1, &act, 0);
sigaction(SIGINT, &act, 0);
/* Set socket server */
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(THERM_SERVER_PORT);
bind(serverFd, (struct sockaddr *) &servaddr, sizeof(servaddr));
listen(serverFd, 5);
/* initialize thermometer api (required by rand api) */
initTherm();
/* get set up to capture client socket address */
cliLen = sizeof(struct sockaddr_in);
startupTime = time(NULL);
printTServerInfo(servaddr, cliLen, startupTime);
Now the block with all the hot action, an infinite while loop for the socket server to accept connections, get the current temperature value and then write to the socket. If a SIGUSR1 or SIGINT (ctrl+c) is received by the handlers the loop value loopCounter will be set to false and the while loop will end allowing for some clean up at the end to close the sockets, semaphores, etc...
/* infinite loop until SIGUSR1 or SIGINT is handled */
while (loopCounter) {
printf("Waiting for Client connection\n");
connectionFd = accept(serverFd, (struct sockaddr*) &cliaAddr, &cliLen);
/* valid connection to client accepted */
if (connectionFd >= 0) {
currentTime = time(NULL);
/*print client address info and time of connection*/
/* http://www.cs.odu.edu/~cs476/fall03/lectures/sockets.htm
*
* Note ctime() seems to build in trailing \n
* */
printf("Client Connected %s:%d at %s",
inet_ntoa(cliaAddr.sin_addr), ntohs(cliaAddr.sin_port),
ctime(¤tTime));
/*Begin Critical Section acquire Semaphore*/
if (semop(therm_sem_id, &semAcquireBuffer, 1) == -1) {
printf("Critical Section Begin failed semid: %d\n",
therm_sem_id);
} else {
printf("Critical Section Begin\n");
/*write current temp in F to connected socket*/
fread = getTemp();
snprintf(tempBuffer, MAX_BUFFER, "%f\n", fread);
/*End Critical Section Release Semaphore*/
if (semop(therm_sem_id, &semReleaseBuffer, 1) == -1) {
printf("Critical Section End failed semid: %d\n",
therm_sem_id);
}
printf("Critical Section End\n");
}
/*write string tempBuffer to connected socket, close connection*/
write(connectionFd, tempBuffer, strlen(tempBuffer));
close(connectionFd);
}
}
The clean up code is reached by the signal handler function changing the loopCounter to allow the while loop to end. Other signals could cause the process to exit, I am not sure what that really means for the still open socket, and more importantly the semaphore. Might there be a better facility in C to handle this case?
/* clean up socket, semaphore before process exits*/
/* TODO better way to do this? ie Java finally{}?*/
printf("Shutting down server\n");
close(serverFd);
semctl(therm_sem_id, 0, IPC_RMID);
return 0;
}
Building therm-server
Invoking: GCC C Compiler - note libtherm and libexp are my custom static libraries
gcc -I"/home/ammianus/workspace/libtherm" -I"/home/ammianus/workspace/libexp" -O0 -g3 -pedantic -Wall -c -fmessage-length=0 -MMD -MP -MF"thermserv.d" -MT"thermserv.d" -o"thermserv.o" "../thermserv.c
Invoking: GCC C Linker
gcc -static -L"/home/ammianus/workspace/libtherm/Debug" -L"/home/ammianus/workspace/libexp/Debug" -o"therm-server" ./thermserv.o -ltherm -lexp
Running therm-server
For the fun part. Open a terminal and navigate to /home/ammianus/workspace/libtherm/Debug
./therm-serv
This launches the server, it acquires the semaphore and inits the socket listening on port 6777
*** Semaphore 131075
*** Thermo Server initialized.
*** cliLen size: 16
*** Server Listening as 0.0.0.0:6777
*** Server PID: 4790
*** Start up time: Sun Oct 31 12:27:11 2010
Waiting for Client connection
To connect to the socket with a client, use telnet or a web browser and connect to 0.0.0.0:6777
ammianus@Hadrian:~$ telnet 0.0.0.0 6777
Trying 0.0.0.0...
Connected to 0.0.0.0.
Escape character is '^]'.
72.720497
Connection closed by foreign host.
Getting 72.720497 is the "temperature" that was our goal from the start!
On the server side it logged this connection attempt:
Waiting for Client connection
Client Connected 127.0.0.1:57090 at Sun Oct 31 12:30:17 2010
Critical Section Begin
Critical Section End
Waiting for Client connection
Etc..
The server will continue to run and accept connections, (try connecting with a browser and holding down 'F5').
To shutdown the server, you can send a signal to its PID
ammianus@Hadrian:~$ kill -s SIGUSR1 4790
And server handles this signal gracefully
*** Got 10 signal from 4794
Shutting down server
References
http://www.suite101.com/content/c-header-files-a2936
http://stackoverflow.com/questions/2406986/c-warning-implicit-declaration-of-function-exit
http://stackoverflow.com/questions/4009090/what-is-correct-way-to-have-single-signal-handler-function-for-multiple-signals
http://www.cs.odu.edu/~cs476/fall03/lectures/sockets.htm
Jones, M. Tim. GNULinux application programming. 2nd ed. Cengage Learning, 2005. Print.
Richard, W., and Stephen A. Advanced Programming in the UNIX Environment. Addison-Wesley Professional, 2008. Print.
Saturday, October 30, 2010
Blogger stats behind the scenes
I had a look at the stats feature that is provided by Blogger for your site automatically. This is the first look I've actually had at the traffic to my site since it began. This is the view that webmasters get into the site traffic:
This has got me thinking about how to increase traffic to this blog, and provide more useful information to the public.
Top Post
Installing Korean support on Windows Mobile 6.5 (Feb 27, 2010)
Distant 2nd place
Java HashMap vs. C# Hashtable (Apr 18, 2009)
Posts on other programming topics, including recent Ubuntu/Linux related posts round out the rest of my "top posts". On the other end posts on Redwoods don't seem very popular :(, and neither do the WorldWind topics (surprise, surprise).
It was very cool to see an audience from across the globe.
Top Ten Countries
Thank you all for reading!
Conclusions
My goal with the blog has always been to document information I consider useful. If it couldn't help others in someway, why bother posting it on a public channel like a blog? To that end I think I should do more to attract visitors and give them more of what they want. Some interpretations of my statistics so far:
This has got me thinking about how to increase traffic to this blog, and provide more useful information to the public.
Top Post
Installing Korean support on Windows Mobile 6.5 (Feb 27, 2010)
Distant 2nd place
Java HashMap vs. C# Hashtable (Apr 18, 2009)
Posts on other programming topics, including recent Ubuntu/Linux related posts round out the rest of my "top posts". On the other end posts on Redwoods don't seem very popular :(, and neither do the WorldWind topics (surprise, surprise).
It was very cool to see an audience from across the globe.
Top Ten Countries
- United States
- France
- Germany
- South Korea
- Canada
- Singapore
- Japan
- Netherlands
- India
- Indonesia
Thank you all for reading!
Conclusions
My goal with the blog has always been to document information I consider useful. If it couldn't help others in someway, why bother posting it on a public channel like a blog? To that end I think I should do more to attract visitors and give them more of what they want. Some interpretations of my statistics so far:
- More content = more page views. I have a backlog of topics I would like to write about. I will make an effort to start putting out these posts on a variety of topics
- More "how-tos", lets be honest, nobody cares that I am growing redwoods in my house. More useful step by step guides to do things applicable to a wider audience (smart phone users, programmers, etc...) may drive traffic from search engines.
- Post design, as a corollary to the previous point. I could change the style of my post. Typically I explain why I worked on a particular problem, then how to solve it, with examples, etc. If I was searching the Internet for some tips on solving a coding problem, I would want the solution to be right up front. I may lead off my posts with the "solution" and let the viewers read on if they like to my back story.
- I will post a sequel to my blockbuster "Korean font on WinMo" post series. I have to give the people what they want.
Saturday, October 23, 2010
Java Sockets
I had the fortunate experience of getting cross-over benefit at work from my unrelated home experiments in Linux (Ubuntu Experiment, 8/1/2010). A few weeks ago I had gone through Chapter 13 of GNU/Linux Application Programming, covering BSD4.4 Sockets API. I had gone through the sample code for the Daytime protocol server/client sockets in C and made my own socket server examples.
At work there was a customer issue related to a workflow engine that appeared to hang on sending emails through the client's SMTP servers. We had some basic questions on whether or not the workflow engine would hang or eventually timeout if it stayed connected to the server? Or cause exceptions?
It dawned on me I could easily set up a socket server that listened on the STMP port and simulated a server not releasing a connection just for a test.
The socket model is basically: Create Socket > Bind to an address > Listen > Accept client connection > [Data Transfer] > Close > loop back to Accept.
Given my recent experience it would be pretty trivial to replace [Data Transfer] with [Delay indefinitely] in order to see what effect that had. Since we use Windows 7 at work, and I didn't have the C code handy, I figured I would just write the same sort of socket server in Java.
I created a new class, SMTPServerSocket
Just a few necessary imports from java.net and javax.net packages.
Definition and default constructor
This was the main body. The socket is created using the ServerSocketFactory. I set it up to listen on port 25 (default SMTP port). I entered an infinite while loop. For the purposes of this demo I didn't build anything in that would allow it to break out of the loop. Ctrl+C seems to do the trick. The socket calls accept() which blocks until a client connects. Then when the client connects I printed some debug info, then put the process to sleep for a long amount of time to test the effects on our workflow engine. After the sleep period the connection is closed.
This actually ended up working ok. We proved that the workflow doesn't appear to have any timeout when it tries to connect to the SMTP server. I could reproduce the behavior of the hanging process while it waited on my SMTPServerSocket during the long sleep.
At work there was a customer issue related to a workflow engine that appeared to hang on sending emails through the client's SMTP servers. We had some basic questions on whether or not the workflow engine would hang or eventually timeout if it stayed connected to the server? Or cause exceptions?
It dawned on me I could easily set up a socket server that listened on the STMP port and simulated a server not releasing a connection just for a test.
The socket model is basically: Create Socket > Bind to an address > Listen > Accept client connection > [Data Transfer] > Close > loop back to Accept.
Given my recent experience it would be pretty trivial to replace [Data Transfer] with [Delay indefinitely] in order to see what effect that had. Since we use Windows 7 at work, and I didn't have the C code handy, I figured I would just write the same sort of socket server in Java.
I created a new class, SMTPServerSocket
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import javax.net.ServerSocketFactory;
Just a few necessary imports from java.net and javax.net packages.
public class SMTPSocketServer
{
public SMTPSocketServer(){
super();
}
Definition and default constructor
public static void main(String[] args)
{
try
{
//get the default server socket factory
ServerSocketFactory socketFactory = ServerSocketFactory.getDefault();
//port 25 - SMTP, allow a backlog of 1000 before connections are refused.
ServerSocket smtpServer = socketFactory.createServerSocket(25, 1000);
System.out.println("Started: "+smtpServer.getLocalSocketAddress().toString());
//TODO need a way to break out of this infinite loop
boolean listen = true;
while(listen){
System.out.println("Waiting for connection "+(new Date()));
Socket sock = smtpServer.accept();
//print some info on the connection
System.out.println("Accepted: "+sock.getInetAddress().toString());
System.out.println("SO_TIMEOUT: "+sock.getSoTimeout());
System.out.println("Data: "+sock.getReceiveBufferSize());
System.out.println("Sleeping "+(new Date()));
//sleep for n * 1000 milliseconds
Thread.sleep(2000 * 1000 );
System.out.println("Done "+(new Date()));
sock.close();
}
//close
smtpServer.close();
}
catch (IOException e)
{
e.printStackTrace();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
This was the main body. The socket is created using the ServerSocketFactory. I set it up to listen on port 25 (default SMTP port). I entered an infinite while loop. For the purposes of this demo I didn't build anything in that would allow it to break out of the loop. Ctrl+C seems to do the trick. The socket calls accept() which blocks until a client connects. Then when the client connects I printed some debug info, then put the process to sleep for a long amount of time to test the effects on our workflow engine. After the sleep period the connection is closed.
This actually ended up working ok. We proved that the workflow doesn't appear to have any timeout when it tries to connect to the SMTP server. I could reproduce the behavior of the hanging process while it waited on my SMTPServerSocket during the long sleep.
Sunday, October 10, 2010
Redwoods - Closeups
Instead of looking at the macro view for these trees, today I thought it would be more interesting to focus on closeups of the seedlings, now a couple months old. The colors are really striking as the stem is now turning a reddish-brown color that contrasts with the various shades of green needles and branches.
Labels:
arboriculture,
redwood
Subclipse SVN plug-in for Eclipse
10/10/10 - had to post something.
I've experiences utter frustration with the SVN command-line client to maintain the various projects I've been working on and the lack of a Linux equivalent to TortoiseSVN. Typical example, I added a directory to the repository by mistake before running a clean operation. It took 20 minutes trying to delete files, cleanup commands to get back to a starting place with the files I really wanted to add.
The only apparent GUI for SVN on Linux appears to be through an Eclipse plug-in. Since I am moving my projects to Eclipse anyway this is a no-brainer.
Subclipse
Subclipse is the Eclipse client that integrates directly with the Eclipse IDE, even my Eclipse CDT IDE works. The download instructions are out of date with the Helios package of Eclipse (or Linux version). But you can more or less follow the instructions they give.
The installation is performed from within Eclipse
1. Start Eclipse
2. Help > Install New Software
3. In the Install wizard, Work with: field, enter the URL for the Subclipse version from the download and install page of the Subclipse site. In my case: http://subclipse.tigris.org/update_1.6.x.
4. I selected all the packages there, Core SVNKit Library, Optional JNA Library, Subclipse.
5. Finish your way through the wizard to install everything.
6. After the install you have to restart Eclipse.
7. I got a JavaHL error on start up of Eclipse, having to do with the JNA library. This is another topic discussed on the Subclipse wiki.
8. I ignored the error and Subclipse worked so I don't know how serious it is. I since went and installed then performed the JavaHL test cases successfully.
9. Note: I already had the SVN subversion command-line client installed, that may be a prerequisite, I am not sure to be honest.
The usage of Subclipse is not that clear-cut and documentation is lacking.
1. You need to set up a repository. Under Window > Open Perspective > Other choose SVN Repository Exploring. You can add a new repository if you have the URL (I had already created a local repository with the command line client, "file:///home/ammianus/svn-repo").
2. From the SVN Repository Explorer perspective, you can right-click a folder in your repository then click Import..., select a project in your workspace and you are good to go.
3. Once a project is associated with a location in your repository, if you right-click on a project or file > Context Menu > Team > * there will be all the normal SVN commands you would need. You will also notice that next to the project name will be the SVN repository location in brackets [].
4. You can see the behavior of Subclipse in a Console view in which you can switch to the SVN console output.
I've experiences utter frustration with the SVN command-line client to maintain the various projects I've been working on and the lack of a Linux equivalent to TortoiseSVN. Typical example, I added a directory to the repository by mistake before running a clean operation. It took 20 minutes trying to delete files, cleanup commands to get back to a starting place with the files I really wanted to add.
The only apparent GUI for SVN on Linux appears to be through an Eclipse plug-in. Since I am moving my projects to Eclipse anyway this is a no-brainer.
Subclipse
Subclipse is the Eclipse client that integrates directly with the Eclipse IDE, even my Eclipse CDT IDE works. The download instructions are out of date with the Helios package of Eclipse (or Linux version). But you can more or less follow the instructions they give.
The installation is performed from within Eclipse
1. Start Eclipse
2. Help > Install New Software
3. In the Install wizard, Work with: field, enter the URL for the Subclipse version from the download and install page of the Subclipse site. In my case: http://subclipse.tigris.org/update_1.6.x.
4. I selected all the packages there, Core SVNKit Library, Optional JNA Library, Subclipse.
5. Finish your way through the wizard to install everything.
6. After the install you have to restart Eclipse.
7. I got a JavaHL error on start up of Eclipse, having to do with the JNA library. This is another topic discussed on the Subclipse wiki.
8. I ignored the error and Subclipse worked so I don't know how serious it is. I since went and installed then performed the JavaHL test cases successfully.
9. Note: I already had the SVN subversion command-line client installed, that may be a prerequisite, I am not sure to be honest.
The usage of Subclipse is not that clear-cut and documentation is lacking.
1. You need to set up a repository. Under Window > Open Perspective > Other choose SVN Repository Exploring. You can add a new repository if you have the URL (I had already created a local repository with the command line client, "file:///home/ammianus/svn-repo").
2. From the SVN Repository Explorer perspective, you can right-click a folder in your repository then click Import..., select a project in your workspace and you are good to go.
3. Once a project is associated with a location in your repository, if you right-click on a project or file > Context Menu > Team > * there will be all the normal SVN commands you would need. You will also notice that next to the project name will be the SVN repository location in brackets [].
4. You can see the behavior of Subclipse in a Console view in which you can switch to the SVN console output.
Subscribe to:
Posts (Atom)