lib: added DatabaseDatabase::SortByRecordCount()
[barry/progweb.git] / src / j_server.cc
blobadef447e96e9c2e897202e6951459a442800df6e
1 ///
2 /// \file j_server.cc
3 /// Server protocol implementation
4 ///
6 /*
7 Copyright (C) 2009, Nicolas VIVIEN
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "j_server.h"
23 #include "protocol.h"
24 #include "data.h"
25 #include "endian.h"
26 #include "debug.h"
27 #include "j_message.h"
28 #include "protostructs.h"
29 #include "record-internal.h"
30 #include "error.h"
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 #include <netdb.h>
39 #include <string.h>
40 #include <errno.h>
42 #include <sstream>
43 #include <algorithm>
45 using namespace std;
47 namespace Barry { namespace JDWP {
49 static void * acceptThread(Barry::Thread::CallbackData *data);
52 JDWServer::JDWServer(Barry::Mode::JVMDebug &device,
53 const char *address, int port)
54 : jvmdebug(&device)
55 , acceptfd(-1)
56 , sockfd(-1)
57 , address(address)
58 , port(port)
59 , loop(false)
60 , targetrunning(false)
61 , printConsoleMessage(0)
63 SearchDebugFile(debugFileList);
67 JDWServer::~JDWServer()
69 Stop();
73 void JDWServer::SetPasswordDevice(string password)
75 this->password = password;
79 void JDWServer::SetConsoleCallback(ConsoleCallbackType callback)
81 printConsoleMessage = callback;
84 static const char* h_strerror(int code)
86 // Codes and messages taken from the Linux gethostbyname(3) manpage
87 switch( code )
89 case HOST_NOT_FOUND:
90 return "HOST_NOT_FOUND: The specified host is unknown";
92 case NO_ADDRESS:
93 return "NO_ADDRESS: The requested name is valid but does not have an IP address";
95 case NO_RECOVERY:
96 return "NO_RECOVERY: A non-recoverable name server error occurred";
98 case TRY_AGAIN:
99 return "TRY_AGAIN: A temporary error occurred on an authoritative name server. Try again later.";
101 default:
102 return "Unknown network error code";
106 bool JDWServer::Start()
108 int rc;
110 struct hostent *hp;
111 struct sockaddr_in sad;
114 memset((char *) &sad, '\0', sizeof(struct sockaddr_in));
116 if (!address.size())
117 sad.sin_addr.s_addr = INADDR_ANY;
118 else {
119 sad.sin_addr.s_addr = inet_addr(address.c_str());
121 if (sad.sin_addr.s_addr == INADDR_NONE) {
122 hp = gethostbyname(address.c_str());
124 if (hp == NULL) {
125 std::ostringstream oss;
126 oss << "JDWServer::Start: " << h_errno << h_strerror(h_errno);
127 throw Barry::Error(oss.str());
130 memcpy((char*) &sad.sin_addr, (char*) hp->h_addr, (size_t) hp->h_length);
134 sad.sin_family = AF_INET;
135 sad.sin_port = htons((short) (port & 0xFFFF));
137 // Open socket
138 sockfd = socket(sad.sin_family, SOCK_STREAM, 0);
140 if (sockfd < 0) {
141 throw Barry::ErrnoError("JDWServer::Start: Cannot open socket.", errno);
144 // Bind
145 rc = bind(sockfd, (struct sockaddr *) &sad, sizeof(sad));
147 if (rc < 0) {
148 int code = errno;
150 close(sockfd);
151 sockfd = -1;
153 throw Barry::ErrnoError("JDWServer::Start: Cannot bind socket", code);
156 // Listen
157 if (listen(sockfd, SOMAXCONN) < 0) {
158 int code = errno;
160 close(sockfd);
161 sockfd = -1;
163 throw Barry::ErrnoError("JDWServer::Start: Cannot listen on socket", code);
166 handler.reset(new Thread(sockfd, acceptThread, (void*) this));
168 return true;
172 static void * acceptThread(Barry::Thread::CallbackData *data)
174 JDWServer *s = (JDWServer *) data->userdata;
176 while( !data->stopflag ) {
177 if( s->AcceptConnection() &&
178 s->AttachToDevice() &&
179 s->InitVisibleClassList() &&
180 s->Hello() )
182 s->Run(data->stopflag);
183 s->DetachFromDevice();
187 return NULL;
191 // Returns true if a new connection was accepted and established
192 bool JDWServer::AcceptConnection()
194 struct sockaddr_in addr;
195 struct sockaddr *sa = (struct sockaddr*) &addr;
196 socklen_t addrlen = sizeof(addr);
198 acceptfd = accept(sockfd, sa, &addrlen);
199 if( acceptfd < 0 )
200 return false;
202 fcntl(acceptfd, F_SETFL, O_NONBLOCK);
203 return true;
207 bool JDWServer::AttachToDevice()
209 targetrunning = false;
211 jvmdebug->Open(password.c_str());
212 jvmdebug->Attach();
214 jvmdebug->Unknown01();
215 jvmdebug->Unknown02();
216 jvmdebug->Unknown03();
217 jvmdebug->Unknown04();
218 jvmdebug->Unknown05();
220 jvmdebug->GetModulesList(modulesList);
221 dout(modulesList);
223 // Check debug info for each modules
224 JVMModulesList::const_iterator b = modulesList.begin();
225 for ( ; b != modulesList.end(); b++) {
226 JDG::CodInfo codInfo;
228 const JVMModulesEntry &entry = *b;
230 bool ret = LoadDebugInfo(debugFileList, entry.UniqueID, entry.Name, codInfo);
232 if (ret == true) {
233 appList[entry.UniqueID].Load(codInfo);
235 else {
236 dout("No debug information found for '" << entry.Name);
237 dout("' (" << hex << setfill('0') << setw(8) << entry.UniqueID << ")." << endl)
241 return true;
245 void JDWServer::DetachFromDevice()
247 jvmdebug->Detach();
248 jvmdebug->Close();
252 #define JDWP_HELLO_STRING "JDWP-Handshake"
256 bool JDWServer::Hello()
258 bool ret;
260 Barry::Data response;
262 const size_t len = strlen(JDWP_HELLO_STRING);
264 JDWMessage msg(acceptfd);
266 do {
267 ret = msg.Receive(response);
269 while (!ret);
271 size_t size = response.GetSize();
272 char *str = (char *) response.GetBuffer();
274 if (size != len)
275 return false;
277 if (!strncmp(str, JDWP_HELLO_STRING, len)) {
278 Data command(JDWP_HELLO_STRING, len);
280 msg.Send(command);
282 return true;
285 return false;
289 void JDWServer::Run(volatile bool &stopflag)
291 string str;
292 JDWMessage msg(acceptfd);
294 Barry::Data command;
296 MAKE_JDWPPACKET(rpack, command);
298 loop = true;
300 while( loop && !stopflag ) {
301 if (targetrunning) {
302 // Read JDWP message from device
303 int value = jvmdebug->GetConsoleMessage(str);
305 if (value < 0) {
306 bool ret;
307 int status;
309 ret = jvmdebug->GetStatus(status);
311 while (!ret) {
312 // Read JDB message from host
313 msg.Receive(command);
315 if (command.GetSize() > 0) {
316 // Convert to packet
317 rpack = (const Barry::Protocol::JDWP::Packet *) command.GetData();
319 if (command.GetSize() != be_btohl(rpack->length)) {
320 dout("Packet size error !!!" << endl);
322 // TODO : add throw exception
324 continue;
327 CommandsetProcess(command);
329 break;
331 else
332 ret = jvmdebug->WaitStatus(status);
335 else {
336 if (printConsoleMessage != NULL)
337 printConsoleMessage(str);
340 else {
341 // Read JDB message from host
342 msg.Receive(command);
344 if (command.GetSize() > 0) {
345 // Convert to packet
346 rpack = (const Barry::Protocol::JDWP::Packet *) command.GetData();
348 if (command.GetSize() != be_btohl(rpack->length)) {
349 dout("Packet size error !!!" << endl);
351 // TODO : add throw exception
353 continue;
356 CommandsetProcess(command);
359 usleep(50);
365 bool JDWServer::Stop()
367 if( handler.get() ) {
368 handler->StopFlag();
369 handler.reset();
372 if( sockfd >= 0 ) {
373 close(sockfd);
374 sockfd = -1;
377 if( acceptfd >= 0 ) {
378 close(acceptfd);
379 acceptfd = -1;
382 return true;
386 bool JDWServer::InitVisibleClassList()
388 int index;
390 // Skip the cell '0'
391 // it's very ugly, but I want use an index started at '1' inside of '0'
392 // JDB works from '1' :(
393 JDG::ClassEntry e;
394 visibleClassList.push_back(e);
396 // Count and index the class (start to '1')
397 index = 1;
398 JDWAppList::iterator it;
400 for (it = appList.begin(); it != appList.end(); it++) {
401 JDWAppInfo &appInfo = it->second;
402 JDG::ClassList &list = appInfo.classList;
404 JDG::ClassList::iterator b;
406 for (b = list.begin(); b != list.end(); b++) {
407 // FIXME
408 // I don't from class field, we have to filter the class view by JDB
409 // if ((b->type != 0x824) && (b->type != 0x64)) {
410 if (b->id == 0xffffffff) {
411 b->index = -1;
413 continue;
416 b->index = index;
418 visibleClassList.push_back(*b);
420 index++;
424 visibleClassList.CreateDefaultEntries();
426 return true;
430 void JDWServer::CommandsetProcess(Data &cmd)
432 MAKE_JDWPPACKET(rpack, cmd);
434 switch (rpack->u.command.commandset) {
435 case JDWP_CMDSET_VIRTUALMACHINE:
436 CommandsetVirtualMachineProcess(cmd);
437 break;
439 case JDWP_CMDSET_REFERECENTYPE:
440 break;
442 case JDWP_CMDSET_CLASSTYPE:
443 break;
445 case JDWP_CMDSET_ARRAYTYPE:
446 break;
448 case JDWP_CMDSET_INTERFACETYPE:
449 break;
451 case JDWP_CMDSET_METHOD:
452 break;
454 case JDWP_CMDSET_FIELD:
455 break;
457 case JDWP_CMDSET_OBJECTREFERENCE:
458 break;
460 case JDWP_CMDSET_STRINGREFERENCE:
461 break;
463 case JDWP_CMDSET_THREADREFERENCE:
464 break;
466 case JDWP_CMDSET_THREADGROUPREFERENCE:
467 break;
469 case JDWP_CMDSET_ARRAYREFERENCE:
470 break;
472 case JDWP_CMDSET_CLASSLOADERREFERENCE:
473 break;
475 case JDWP_CMDSET_EVENTREQUEST:
476 CommandsetEventRequestProcess(cmd);
477 break;
479 case JDWP_CMDSET_STACKFRAME:
480 break;
482 case JDWP_CMDSET_CLASSOBJECTREFERENCE:
483 break;
485 case JDWP_CMDSET_EVENT:
486 break;
488 default:
489 // TODO : add exception (or alert)
490 dout("Commandset unknown !!!" << endl);
495 void JDWServer::CommandsetVirtualMachineProcess(Data &cmd)
497 MAKE_JDWPPACKET(rpack, cmd);
499 switch (rpack->u.command.command) {
500 case JDWP_CMD_VERSION:
501 CommandVersion(cmd);
502 break;
504 case JDWP_CMD_ALLCLASSES:
505 CommandAllClasses(cmd);
506 break;
508 case JDWP_CMD_ALLTHREADS:
509 CommandAllThreads(cmd);
510 break;
512 case JDWP_CMD_DISPOSE:
513 loop = false;
514 targetrunning = false;
515 close(acceptfd);
516 acceptfd = -1;
517 break;
519 case JDWP_CMD_IDSIZES:
520 CommandIdSizes(cmd);
521 break;
523 case JDWP_CMD_SUSPEND:
524 CommandSuspend(cmd);
525 targetrunning = false;
526 break;
528 case JDWP_CMD_RESUME:
529 CommandResume(cmd);
530 targetrunning = true;
531 break;
533 case JDWP_CMD_CLASSPATHS:
534 CommandClassPaths(cmd);
535 break;
540 void JDWServer::CommandsetEventRequestProcess(Data &cmd)
542 MAKE_JDWPPACKET(rpack, cmd);
544 switch (rpack->u.command.command) {
545 case JDWP_CMD_SET:
546 CommandSet(cmd);
547 break;
552 void JDWServer::CommandVersion(Data &cmd)
554 JDWMessage msg(acceptfd);
556 // Build packet data
557 Data response;
559 size_t offset = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE;
561 AddJDWString(response, offset, string("RIM JVM"));
562 AddJDWInt(response, offset, be_htobl(1));
563 AddJDWInt(response, offset, be_htobl(4));
564 AddJDWString(response, offset, string("1.4"));
565 AddJDWString(response, offset, string("RIM JVM"));
567 response.ReleaseBuffer(offset);
570 size_t total_size = response.GetSize();
572 // Fill in the header values
573 MAKE_JDWPPACKETPTR_BUF(cpack, response.GetBuffer(total_size));
574 Barry::Protocol::JDWP::Packet &packet = *cpack;
577 MAKE_JDWPPACKET(rpack, cmd);
579 packet.length = be_htobl(total_size);
580 packet.id = rpack->id;
581 packet.flags = 0x80;
582 packet.u.response.errorcode = be_htobs(0);
584 response.ReleaseBuffer(total_size);
585 msg.Send(response);
589 void JDWServer::CommandAllClasses(Data &cmd)
591 size_t i;
592 int size;
594 JDWMessage msg(acceptfd);
596 // Build packet data
597 Data response;
599 size_t offset = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE;
601 // Size of known class list
602 size = visibleClassList.size() - 1;
604 AddJDWInt(response, offset, be_htobl(size));
606 // Then, write the list of known class
607 for (i=1; i<visibleClassList.size(); i++) {
608 string str = visibleClassList[i].GetFullClassName();
610 str = "L" + str + ";";
611 replace_if(str.begin(), str.end(), bind2nd(equal_to<char>(),'.'), '/');
613 AddJDWByte(response, offset, 0x01);
614 AddJDWInt(response, offset, i); // Should be equal to visibleClassList[i].index
615 AddJDWString(response, offset, str);
616 AddJDWInt(response, offset, be_htobl(0x04));
619 response.ReleaseBuffer(offset);
622 size_t total_size = response.GetSize();
624 // Fill in the header values
625 MAKE_JDWPPACKETPTR_BUF(cpack, response.GetBuffer(total_size));
626 Barry::Protocol::JDWP::Packet &packet = *cpack;
629 MAKE_JDWPPACKET(rpack, cmd);
631 packet.length = be_htobl(total_size);
632 packet.id = rpack->id;
633 packet.flags = 0x80;
634 packet.u.response.errorcode = be_htobs(0);
636 response.ReleaseBuffer(total_size);
637 msg.Send(response);
641 void JDWServer::CommandAllThreads(Data &cmd)
643 JDWMessage msg(acceptfd);
645 // Get threads list from device
646 JVMThreadsList list;
647 jvmdebug->GetThreadsList(list);
648 dout(list);
650 // Build packet data
651 Data response;
653 size_t offset = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE;
655 // Indicate the number of element
656 AddJDWInt(response, offset, be_htobl(list.size()));
658 // Send all threads ID
659 JVMThreadsList::const_iterator b = list.begin();
660 for( ; b != list.end(); b++ ) {
661 const JVMThreadsEntry &entry = *b;
663 AddJDWInt(response, offset, be_htobl(entry.Id));
666 response.ReleaseBuffer(offset);
669 size_t total_size = response.GetSize();
671 // Fill in the header values
672 MAKE_JDWPPACKETPTR_BUF(cpack, response.GetBuffer(total_size));
673 Barry::Protocol::JDWP::Packet &packet = *cpack;
676 MAKE_JDWPPACKET(rpack, cmd);
678 packet.length = be_htobl(total_size);
679 packet.id = rpack->id;
680 packet.flags = 0x80;
681 packet.u.response.errorcode = be_htobs(0);
683 response.ReleaseBuffer(total_size);
684 msg.Send(response);
688 void JDWServer::CommandIdSizes(Data &cmd)
690 JDWMessage msg(acceptfd);
692 MAKE_JDWPPACKET(rpack, cmd);
694 size_t size;
696 Barry::Protocol::JDWP::Packet packet;
698 size = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE
699 + JDWP_PACKETVIRTUALMACHINEIDSIZES_DATA_SIZE;
701 packet.length = be_htobl(size);
702 packet.id = rpack->id;
703 packet.flags = 0x80;
704 packet.u.response.errorcode = be_htobs(0);
705 packet.u.response.u.virtualMachine.u.IDSizes.fieldIDSize = be_htobl(0x04);
706 packet.u.response.u.virtualMachine.u.IDSizes.methodIDSize = be_htobl(0x04);
707 packet.u.response.u.virtualMachine.u.IDSizes.objectIDSize = be_htobl(0x04);
708 packet.u.response.u.virtualMachine.u.IDSizes.referenceTypeIDSize = be_htobl(0x04);
709 packet.u.response.u.virtualMachine.u.IDSizes.frameIDSize = be_htobl(0x04);
711 Data response(&packet, size);
713 msg.Send(response);
717 void JDWServer::CommandSuspend(Data &cmd)
719 JDWMessage msg(acceptfd);
722 // Suspend device
723 jvmdebug->Stop();
725 // Notify debugger
726 MAKE_JDWPPACKET(rpack, cmd);
728 size_t size;
730 Barry::Protocol::JDWP::Packet packet;
732 size = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE;
734 packet.length = be_htobl(size);
735 packet.id = rpack->id;
736 packet.flags = 0x80;
737 packet.u.response.errorcode = be_htobs(0);
739 Data response(&packet, size);
741 msg.Send(response);
745 void JDWServer::CommandResume(Data &cmd)
747 JDWMessage msg(acceptfd);
750 // Resume device
751 jvmdebug->Unknown06();
752 jvmdebug->Unknown07();
753 jvmdebug->Unknown08();
754 jvmdebug->Unknown09();
755 jvmdebug->Unknown10();
756 jvmdebug->Go();
758 // Notify debugger
759 MAKE_JDWPPACKET(rpack, cmd);
761 size_t size;
763 Barry::Protocol::JDWP::Packet packet;
765 size = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE;
767 packet.length = be_htobl(size);
768 packet.id = rpack->id;
769 packet.flags = 0x80;
770 packet.u.response.errorcode = be_htobs(0);
772 Data response(&packet, size);
774 msg.Send(response);
778 void JDWServer::CommandClassPaths(Data &cmd)
780 JDWMessage msg(acceptfd);
782 // Build packet data
783 Data response;
785 size_t offset = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE;
787 AddJDWString(response, offset, string(""));
788 AddJDWInt(response, offset, be_htobl(0));
789 AddJDWInt(response, offset, be_htobl(0));
791 response.ReleaseBuffer(offset);
794 size_t total_size = response.GetSize();
796 // Fill in the header values
797 MAKE_JDWPPACKETPTR_BUF(cpack, response.GetBuffer(total_size));
798 Barry::Protocol::JDWP::Packet &packet = *cpack;
801 MAKE_JDWPPACKET(rpack, cmd);
803 packet.length = be_htobl(total_size);
804 packet.id = rpack->id;
805 packet.flags = 0x80;
806 packet.u.response.errorcode = be_htobs(0);
808 response.ReleaseBuffer(total_size);
809 msg.Send(response);
814 void JDWServer::CommandSet(Data &cmd)
816 static int value = 2;
818 JDWMessage msg(acceptfd);
820 MAKE_JDWPPACKET(rpack, cmd);
822 size_t size;
824 Barry::Protocol::JDWP::Packet packet;
826 size = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE + sizeof(uint32_t);
828 packet.length = be_htobl(size);
829 packet.id = rpack->id;
830 packet.flags = 0x80;
831 packet.u.response.errorcode = be_htobs(0);
832 packet.u.response.u.value = be_htobl(value);
834 Data response(&packet, size);
836 msg.Send(response);
838 value++;
842 }} // namespace Barry::JDWP