menu: added new Keywords tag to .desktop files
[barry.git] / src / j_server.cc
blob33dfabc5ccba4653a5fd31185b5433deff4db0b3
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 "i18n.h"
23 #include "j_server.h"
24 #include "protocol.h"
25 #include "data.h"
26 #include "endian.h"
27 #include "debug.h"
28 #include "j_message.h"
29 #include "protostructs.h"
30 #include "record-internal.h"
31 #include "error.h"
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39 #include <netdb.h>
40 #include <string.h>
41 #include <errno.h>
43 #include <sstream>
44 #include <algorithm>
46 using namespace std;
48 namespace Barry { namespace JDWP {
50 static void * acceptThread(Barry::Thread::CallbackData *data);
53 JDWServer::JDWServer(Barry::Mode::JVMDebug &device,
54 const char *address, int port)
55 : jvmdebug(&device)
56 , acceptfd(-1)
57 , sockfd(-1)
58 , address(address)
59 , port(port)
60 , loop(false)
61 , targetrunning(false)
62 , printConsoleMessage(0)
64 SearchDebugFile(debugFileList);
68 JDWServer::~JDWServer()
70 Stop();
74 void JDWServer::SetPasswordDevice(string password)
76 this->password = password;
80 void JDWServer::SetConsoleCallback(ConsoleCallbackType callback)
82 printConsoleMessage = callback;
85 static const char* h_strerror(int code)
87 // Codes and messages taken from the Linux gethostbyname(3) manpage
88 switch( code )
90 case HOST_NOT_FOUND:
91 return _("HOST_NOT_FOUND: The specified host is unknown");
93 case NO_ADDRESS:
94 return _("NO_ADDRESS: The requested name is valid but does not have an IP address");
96 case NO_RECOVERY:
97 return _("NO_RECOVERY: A non-recoverable name server error occurred");
99 case TRY_AGAIN:
100 return _("TRY_AGAIN: A temporary error occurred on an authoritative name server. Try again later.");
102 default:
103 return _("Unknown network error code");
107 bool JDWServer::Start()
109 int rc;
111 struct hostent *hp;
112 struct sockaddr_in sad;
115 memset((char *) &sad, '\0', sizeof(struct sockaddr_in));
117 if (!address.size())
118 sad.sin_addr.s_addr = INADDR_ANY;
119 else {
120 sad.sin_addr.s_addr = inet_addr(address.c_str());
122 if (sad.sin_addr.s_addr == INADDR_NONE) {
123 hp = gethostbyname(address.c_str());
125 if (hp == NULL) {
126 std::ostringstream oss;
127 oss << "JDWServer::Start: " << h_errno << h_strerror(h_errno);
128 throw Barry::Error(oss.str());
131 memcpy((char*) &sad.sin_addr, (char*) hp->h_addr, (size_t) hp->h_length);
135 sad.sin_family = AF_INET;
136 sad.sin_port = htons((short) (port & 0xFFFF));
138 // Open socket
139 sockfd = socket(sad.sin_family, SOCK_STREAM, 0);
141 if (sockfd < 0) {
142 throw Barry::ErrnoError(_("JDWServer::Start: Cannot open socket."), errno);
145 // Bind
146 rc = bind(sockfd, (struct sockaddr *) &sad, sizeof(sad));
148 if (rc < 0) {
149 int code = errno;
151 close(sockfd);
152 sockfd = -1;
154 throw Barry::ErrnoError(_("JDWServer::Start: Cannot bind socket"), code);
157 // Listen
158 if (listen(sockfd, SOMAXCONN) < 0) {
159 int code = errno;
161 close(sockfd);
162 sockfd = -1;
164 throw Barry::ErrnoError(_("JDWServer::Start: Cannot listen on socket"), code);
167 handler.reset(new Thread(sockfd, acceptThread, (void*) this));
169 return true;
173 static void * acceptThread(Barry::Thread::CallbackData *data)
175 JDWServer *s = (JDWServer *) data->userdata;
177 while( !data->stopflag ) {
178 if( s->AcceptConnection() &&
179 s->AttachToDevice() &&
180 s->InitVisibleClassList() &&
181 s->Hello() )
183 s->Run(data->stopflag);
184 s->DetachFromDevice();
188 return NULL;
192 // Returns true if a new connection was accepted and established
193 bool JDWServer::AcceptConnection()
195 struct sockaddr_in addr;
196 struct sockaddr *sa = (struct sockaddr*) &addr;
197 socklen_t addrlen = sizeof(addr);
199 acceptfd = accept(sockfd, sa, &addrlen);
200 if( acceptfd < 0 )
201 return false;
203 fcntl(acceptfd, F_SETFL, O_NONBLOCK);
204 return true;
208 bool JDWServer::AttachToDevice()
210 targetrunning = false;
212 jvmdebug->Open(password.c_str());
213 jvmdebug->Attach();
215 jvmdebug->Unknown01();
216 jvmdebug->Unknown02();
217 jvmdebug->Unknown03();
218 jvmdebug->Unknown04();
219 jvmdebug->Unknown05();
221 jvmdebug->GetModulesList(modulesList);
222 dout(modulesList);
224 // Check debug info for each modules
225 JVMModulesList::const_iterator b = modulesList.begin();
226 for ( ; b != modulesList.end(); b++) {
227 JDG::CodInfo codInfo;
229 const JVMModulesEntry &entry = *b;
231 bool ret = LoadDebugInfo(debugFileList, entry.UniqueID, entry.Name, codInfo);
233 if (ret == true) {
234 appList[entry.UniqueID].Load(codInfo);
236 else {
237 dout(_("No debug information found for: ") << entry.Name);
238 dout("' (" << hex << setfill('0') << setw(8) << entry.UniqueID << ")." << endl)
242 return true;
246 void JDWServer::DetachFromDevice()
248 jvmdebug->Detach();
249 jvmdebug->Close();
253 #define JDWP_HELLO_STRING "JDWP-Handshake"
257 bool JDWServer::Hello()
259 bool ret;
261 Barry::Data response;
263 const size_t len = strlen(JDWP_HELLO_STRING);
265 JDWMessage msg(acceptfd);
267 do {
268 ret = msg.Receive(response);
270 while (!ret);
272 size_t size = response.GetSize();
273 char *str = (char *) response.GetBuffer();
275 if (size != len)
276 return false;
278 if (!strncmp(str, JDWP_HELLO_STRING, len)) {
279 Data command(JDWP_HELLO_STRING, len);
281 msg.Send(command);
283 return true;
286 return false;
290 void JDWServer::Run(volatile bool &stopflag)
292 string str;
293 JDWMessage msg(acceptfd);
295 Barry::Data command;
297 MAKE_JDWPPACKET(rpack, command);
299 loop = true;
301 while( loop && !stopflag ) {
302 if (targetrunning) {
303 // Read JDWP message from device
304 int value = jvmdebug->GetConsoleMessage(str);
306 if (value < 0) {
307 bool ret;
308 int status;
310 ret = jvmdebug->GetStatus(status);
312 while (!ret) {
313 // Read JDB message from host
314 msg.Receive(command);
316 if (command.GetSize() > 0) {
317 // Convert to packet
318 rpack = (const Barry::Protocol::JDWP::Packet *) command.GetData();
320 if (command.GetSize() != be_btohl(rpack->length)) {
321 dout(_("Packet size error !!!") << endl);
323 // TODO : add throw exception
325 continue;
328 CommandsetProcess(command);
330 break;
332 else
333 ret = jvmdebug->WaitStatus(status);
336 else {
337 if (printConsoleMessage != NULL)
338 printConsoleMessage(str);
341 else {
342 // Read JDB message from host
343 msg.Receive(command);
345 if (command.GetSize() > 0) {
346 // Convert to packet
347 rpack = (const Barry::Protocol::JDWP::Packet *) command.GetData();
349 if (command.GetSize() != be_btohl(rpack->length)) {
350 dout(_("Packet size error !!!") << endl);
352 // TODO : add throw exception
354 continue;
357 CommandsetProcess(command);
360 usleep(50);
366 bool JDWServer::Stop()
368 if( handler.get() ) {
369 handler->StopFlag();
370 handler.reset();
373 if( sockfd >= 0 ) {
374 close(sockfd);
375 sockfd = -1;
378 if( acceptfd >= 0 ) {
379 close(acceptfd);
380 acceptfd = -1;
383 return true;
387 bool JDWServer::InitVisibleClassList()
389 int index;
391 // Skip the cell '0'
392 // it's very ugly, but I want use an index started at '1' inside of '0'
393 // JDB works from '1' :(
394 JDG::ClassEntry e;
395 visibleClassList.push_back(e);
397 // Count and index the class (start to '1')
398 index = 1;
399 JDWAppList::iterator it;
401 for (it = appList.begin(); it != appList.end(); it++) {
402 JDWAppInfo &appInfo = it->second;
403 JDG::ClassList &list = appInfo.classList;
405 JDG::ClassList::iterator b;
407 for (b = list.begin(); b != list.end(); b++) {
408 // FIXME
409 // I don't from class field, we have to filter the class view by JDB
410 // if ((b->type != 0x824) && (b->type != 0x64)) {
411 if (b->id == 0xffffffff) {
412 b->index = -1;
414 continue;
417 b->index = index;
419 visibleClassList.push_back(*b);
421 index++;
425 visibleClassList.CreateDefaultEntries();
427 return true;
431 void JDWServer::CommandsetProcess(Data &cmd)
433 MAKE_JDWPPACKET(rpack, cmd);
435 switch (rpack->u.command.commandset) {
436 case JDWP_CMDSET_VIRTUALMACHINE:
437 CommandsetVirtualMachineProcess(cmd);
438 break;
440 case JDWP_CMDSET_REFERECENTYPE:
441 break;
443 case JDWP_CMDSET_CLASSTYPE:
444 break;
446 case JDWP_CMDSET_ARRAYTYPE:
447 break;
449 case JDWP_CMDSET_INTERFACETYPE:
450 break;
452 case JDWP_CMDSET_METHOD:
453 break;
455 case JDWP_CMDSET_FIELD:
456 break;
458 case JDWP_CMDSET_OBJECTREFERENCE:
459 break;
461 case JDWP_CMDSET_STRINGREFERENCE:
462 break;
464 case JDWP_CMDSET_THREADREFERENCE:
465 break;
467 case JDWP_CMDSET_THREADGROUPREFERENCE:
468 break;
470 case JDWP_CMDSET_ARRAYREFERENCE:
471 break;
473 case JDWP_CMDSET_CLASSLOADERREFERENCE:
474 break;
476 case JDWP_CMDSET_EVENTREQUEST:
477 CommandsetEventRequestProcess(cmd);
478 break;
480 case JDWP_CMDSET_STACKFRAME:
481 break;
483 case JDWP_CMDSET_CLASSOBJECTREFERENCE:
484 break;
486 case JDWP_CMDSET_EVENT:
487 break;
489 default:
490 // TODO : add exception (or alert)
491 dout(_("Commandset unknown !!!") << endl);
496 void JDWServer::CommandsetVirtualMachineProcess(Data &cmd)
498 MAKE_JDWPPACKET(rpack, cmd);
500 switch (rpack->u.command.command) {
501 case JDWP_CMD_VERSION:
502 CommandVersion(cmd);
503 break;
505 case JDWP_CMD_ALLCLASSES:
506 CommandAllClasses(cmd);
507 break;
509 case JDWP_CMD_ALLTHREADS:
510 CommandAllThreads(cmd);
511 break;
513 case JDWP_CMD_DISPOSE:
514 loop = false;
515 targetrunning = false;
516 close(acceptfd);
517 acceptfd = -1;
518 break;
520 case JDWP_CMD_IDSIZES:
521 CommandIdSizes(cmd);
522 break;
524 case JDWP_CMD_SUSPEND:
525 CommandSuspend(cmd);
526 targetrunning = false;
527 break;
529 case JDWP_CMD_RESUME:
530 CommandResume(cmd);
531 targetrunning = true;
532 break;
534 case JDWP_CMD_CLASSPATHS:
535 CommandClassPaths(cmd);
536 break;
541 void JDWServer::CommandsetEventRequestProcess(Data &cmd)
543 MAKE_JDWPPACKET(rpack, cmd);
545 switch (rpack->u.command.command) {
546 case JDWP_CMD_SET:
547 CommandSet(cmd);
548 break;
553 void JDWServer::CommandVersion(Data &cmd)
555 JDWMessage msg(acceptfd);
557 // Build packet data
558 Data response;
560 size_t offset = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE;
562 AddJDWString(response, offset, string("RIM JVM"));
563 AddJDWInt(response, offset, be_htobl(1));
564 AddJDWInt(response, offset, be_htobl(4));
565 AddJDWString(response, offset, string("1.4"));
566 AddJDWString(response, offset, string("RIM JVM"));
568 response.ReleaseBuffer(offset);
571 size_t total_size = response.GetSize();
573 // Fill in the header values
574 MAKE_JDWPPACKETPTR_BUF(cpack, response.GetBuffer(total_size));
575 Barry::Protocol::JDWP::Packet &packet = *cpack;
578 MAKE_JDWPPACKET(rpack, cmd);
580 packet.length = be_htobl(total_size);
581 packet.id = rpack->id;
582 packet.flags = 0x80;
583 packet.u.response.errorcode = be_htobs(0);
585 response.ReleaseBuffer(total_size);
586 msg.Send(response);
590 void JDWServer::CommandAllClasses(Data &cmd)
592 size_t i;
593 int size;
595 JDWMessage msg(acceptfd);
597 // Build packet data
598 Data response;
600 size_t offset = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE;
602 // Size of known class list
603 size = visibleClassList.size() - 1;
605 AddJDWInt(response, offset, be_htobl(size));
607 // Then, write the list of known class
608 for (i=1; i<visibleClassList.size(); i++) {
609 string str = visibleClassList[i].GetFullClassName();
611 str = "L" + str + ";";
612 replace_if(str.begin(), str.end(), bind2nd(equal_to<char>(),'.'), '/');
614 AddJDWByte(response, offset, 0x01);
615 AddJDWInt(response, offset, i); // Should be equal to visibleClassList[i].index
616 AddJDWString(response, offset, str);
617 AddJDWInt(response, offset, be_htobl(0x04));
620 response.ReleaseBuffer(offset);
623 size_t total_size = response.GetSize();
625 // Fill in the header values
626 MAKE_JDWPPACKETPTR_BUF(cpack, response.GetBuffer(total_size));
627 Barry::Protocol::JDWP::Packet &packet = *cpack;
630 MAKE_JDWPPACKET(rpack, cmd);
632 packet.length = be_htobl(total_size);
633 packet.id = rpack->id;
634 packet.flags = 0x80;
635 packet.u.response.errorcode = be_htobs(0);
637 response.ReleaseBuffer(total_size);
638 msg.Send(response);
642 void JDWServer::CommandAllThreads(Data &cmd)
644 JDWMessage msg(acceptfd);
646 // Get threads list from device
647 JVMThreadsList list;
648 jvmdebug->GetThreadsList(list);
649 dout(list);
651 // Build packet data
652 Data response;
654 size_t offset = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE;
656 // Indicate the number of element
657 AddJDWInt(response, offset, be_htobl(list.size()));
659 // Send all threads ID
660 JVMThreadsList::const_iterator b = list.begin();
661 for( ; b != list.end(); b++ ) {
662 const JVMThreadsEntry &entry = *b;
664 AddJDWInt(response, offset, be_htobl(entry.Id));
667 response.ReleaseBuffer(offset);
670 size_t total_size = response.GetSize();
672 // Fill in the header values
673 MAKE_JDWPPACKETPTR_BUF(cpack, response.GetBuffer(total_size));
674 Barry::Protocol::JDWP::Packet &packet = *cpack;
677 MAKE_JDWPPACKET(rpack, cmd);
679 packet.length = be_htobl(total_size);
680 packet.id = rpack->id;
681 packet.flags = 0x80;
682 packet.u.response.errorcode = be_htobs(0);
684 response.ReleaseBuffer(total_size);
685 msg.Send(response);
689 void JDWServer::CommandIdSizes(Data &cmd)
691 JDWMessage msg(acceptfd);
693 MAKE_JDWPPACKET(rpack, cmd);
695 size_t size;
697 Barry::Protocol::JDWP::Packet packet;
699 size = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE
700 + JDWP_PACKETVIRTUALMACHINEIDSIZES_DATA_SIZE;
702 packet.length = be_htobl(size);
703 packet.id = rpack->id;
704 packet.flags = 0x80;
705 packet.u.response.errorcode = be_htobs(0);
706 packet.u.response.u.virtualMachine.u.IDSizes.fieldIDSize = be_htobl(0x04);
707 packet.u.response.u.virtualMachine.u.IDSizes.methodIDSize = be_htobl(0x04);
708 packet.u.response.u.virtualMachine.u.IDSizes.objectIDSize = be_htobl(0x04);
709 packet.u.response.u.virtualMachine.u.IDSizes.referenceTypeIDSize = be_htobl(0x04);
710 packet.u.response.u.virtualMachine.u.IDSizes.frameIDSize = be_htobl(0x04);
712 Data response(&packet, size);
714 msg.Send(response);
718 void JDWServer::CommandSuspend(Data &cmd)
720 JDWMessage msg(acceptfd);
723 // Suspend device
724 jvmdebug->Stop();
726 // Notify debugger
727 MAKE_JDWPPACKET(rpack, cmd);
729 size_t size;
731 Barry::Protocol::JDWP::Packet packet;
733 size = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE;
735 packet.length = be_htobl(size);
736 packet.id = rpack->id;
737 packet.flags = 0x80;
738 packet.u.response.errorcode = be_htobs(0);
740 Data response(&packet, size);
742 msg.Send(response);
746 void JDWServer::CommandResume(Data &cmd)
748 JDWMessage msg(acceptfd);
751 // Resume device
752 jvmdebug->Unknown06();
753 jvmdebug->Unknown07();
754 jvmdebug->Unknown08();
755 jvmdebug->Unknown09();
756 jvmdebug->Unknown10();
757 jvmdebug->Go();
759 // Notify debugger
760 MAKE_JDWPPACKET(rpack, cmd);
762 size_t size;
764 Barry::Protocol::JDWP::Packet packet;
766 size = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE;
768 packet.length = be_htobl(size);
769 packet.id = rpack->id;
770 packet.flags = 0x80;
771 packet.u.response.errorcode = be_htobs(0);
773 Data response(&packet, size);
775 msg.Send(response);
779 void JDWServer::CommandClassPaths(Data &cmd)
781 JDWMessage msg(acceptfd);
783 // Build packet data
784 Data response;
786 size_t offset = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE;
788 AddJDWString(response, offset, string(""));
789 AddJDWInt(response, offset, be_htobl(0));
790 AddJDWInt(response, offset, be_htobl(0));
792 response.ReleaseBuffer(offset);
795 size_t total_size = response.GetSize();
797 // Fill in the header values
798 MAKE_JDWPPACKETPTR_BUF(cpack, response.GetBuffer(total_size));
799 Barry::Protocol::JDWP::Packet &packet = *cpack;
802 MAKE_JDWPPACKET(rpack, cmd);
804 packet.length = be_htobl(total_size);
805 packet.id = rpack->id;
806 packet.flags = 0x80;
807 packet.u.response.errorcode = be_htobs(0);
809 response.ReleaseBuffer(total_size);
810 msg.Send(response);
815 void JDWServer::CommandSet(Data &cmd)
817 static int value = 2;
819 JDWMessage msg(acceptfd);
821 MAKE_JDWPPACKET(rpack, cmd);
823 size_t size;
825 Barry::Protocol::JDWP::Packet packet;
827 size = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE + sizeof(uint32_t);
829 packet.length = be_htobl(size);
830 packet.id = rpack->id;
831 packet.flags = 0x80;
832 packet.u.response.errorcode = be_htobs(0);
833 packet.u.response.u.value = be_htobl(value);
835 Data response(&packet, size);
837 msg.Send(response);
839 value++;
843 }} // namespace Barry::JDWP