To enable a function in Nexus 9, I wrote a driver in the linux kernel, as shown in the following. This driver basically can write values to a specified register. The driver is loaded using module_param_cb().
However, the kernel log shows that this driver is not actually loaded when the kernel starts. Does anyone know the reason, please? I am a totally freshman to linux kernel. Hope someone could provide any suggestions or solutions.
#ifdef CONFIG_PASR
static bool tegra132_is_lpddr3(void)
{
return (tegra_emc_get_dram_type() == DRAM_TYPE_LPDDR2);
}
static void tegra132_pasr_apply_mask(u16 *mem_reg, void *cookie)
{
u32 val = 0;
int device = (int)cookie;
val = TEGRA_EMC_MODE_REG_16 | *mem_reg;
val |= device << TEGRA_EMC_MRW_DEV_SHIFT;
emc_writel(val, EMC_MRW);
pr_debug("%s: cookie = %d mem_reg = 0x%04x val = 0x%08x\n", __func__,
(int)cookie, *mem_reg, val);
}
static void tegra132_pasr_remove_mask(phys_addr_t base, void *cookie)
{
u16 mem_reg = 0;
if (!pasr_register_mask_function(base, NULL, cookie))
tegra132_pasr_apply_mask(&mem_reg, cookie);
}
static int tegra132_pasr_set_mask(phys_addr_t base, void *cookie)
{
return pasr_register_mask_function(base, &tegra132_pasr_apply_mask,
cookie);
}
static int tegra132_pasr_enable(const char *arg, const struct kernel_param *kp)
{
unsigned int old_pasr_enable;
void *cookie;
int num_devices;
u64 device_size;
u64 size_mul;
int ret = 0;
if (!tegra132_is_lpddr3())
return -ENOSYS;
old_pasr_enable = pasr_enable;
param_set_int(arg, kp);
if (old_pasr_enable == pasr_enable)
return ret;
num_devices = 1 << (mc_readl(MC_EMEM_ADR_CFG) & BIT(0));
size_mul = 1 << ((emc_readl(EMC_FBIO_CFG5) >> 4) & BIT(0));
/* Cookie represents the device number to write to MRW register.
* 0x2 to for only dev0, 0x1 for dev1.
*/
if (pasr_enable == 0) {
cookie = (void *)(int)TEGRA_EMC_MRW_DEV1;
tegra132_pasr_remove_mask(TEGRA_DRAM_BASE, cookie);
if (num_devices == 1)
goto exit;
cookie = (void *)(int)TEGRA_EMC_MRW_DEV2;
/* Next device is located after first device, so read DEV0 size
* to decide base address for DEV1 */
device_size = 1 << ((mc_readl(MC_EMEM_ADR_CFG_DEV0) >>
MC_EMEM_DEV_SIZE_SHIFT) &
MC_EMEM_DEV_SIZE_MASK);
device_size = device_size * size_mul * SZ_4M;
tegra132_pasr_remove_mask(TEGRA_DRAM_BASE + device_size, cookie);
} else {
cookie = (void *)(int)TEGRA_EMC_MRW_DEV1;
ret = tegra132_pasr_set_mask(TEGRA_DRAM_BASE, cookie);
if (num_devices == 1 || ret)
goto exit;
cookie = (void *)(int)TEGRA_EMC_MRW_DEV2;
/* Next device is located after first device, so read DEV0 size
* to decide base address for DEV1 */
device_size = 1 << ((mc_readl(MC_EMEM_ADR_CFG_DEV0) >>
MC_EMEM_DEV_SIZE_SHIFT) &
MC_EMEM_DEV_SIZE_MASK);
device_size = device_size * size_mul * SZ_4M;
ret = tegra132_pasr_set_mask(TEGRA_DRAM_BASE + device_size, cookie);
}
exit:
return ret;
}
static struct kernel_param_ops tegra132_pasr_enable_ops = {
.set = tegra132_pasr_enable,
.get = param_get_int,
};
module_param_cb(pasr_enable, &tegra132_pasr_enable_ops, &pasr_enable, 0644);
#endif
Thanks in advance!
Related
I am working on making a customized bsp based on AOSP Nougat latest source.
Android service process ask service manager to find or add the service.
And service manager try to check mac permissions by calling svc_can_register() or svc_can_find() which calls check_mac_perms() which calls getpidcon().
Let's see svc_can_find()
static int svc_can_find(const uint16_t *name, size_t name_len, pid_t spid, uid_t uid)
{
const char *perm = "find";
return check_mac_perms_from_lookup(spid, uid, perm, str8(name, name_len)) ? 1 : 0;
}
check_mac_perms_from_lookup() is like this:
static bool check_mac_perms_from_lookup(pid_t spid, uid_t uid, const char *perm, const char *name)
{
bool allowed;
char *tctx = NULL;
if (selinux_enabled <= 0) {
return true;
}
if (!sehandle) {
ALOGE("SELinux: Failed to find sehandle. Aborting service_manager.\n");
abort();
}
if (selabel_lookup(sehandle, &tctx, name, 0) != 0) {
ALOGE("SELinux: No match for %s in service_contexts.\n", name);
return false;
}
allowed = check_mac_perms(spid, uid, tctx, perm, name);
freecon(tctx);
return allowed;
}
It calls check_mac_perms(). check_mac_perms() like this:
static bool check_mac_perms(pid_t spid, uid_t uid, const char *tctx, const char *perm, const char *name)
{
char *sctx = NULL;
const char *class = "service_manager";
bool allowed;
struct audit_data ad;
if (getpidcon(spid, &sctx) < 0) {
ALOGE("SELinux: getpidcon(pid=%d) failed to retrieve pid context.\n", spid);
return false;
}
ad.pid = spid;
ad.uid = uid;
ad.name = name;
int result = selinux_check_access(sctx, tctx, class, perm, (void *) &ad);
allowed = (result == 0);
freecon(sctx);
return allowed;
}
It calls getpidcon(). getpidcon() is defined in
external/selinux/libselinux/src/procattr.c
getpidcon() is defined like this:
#define getpidattr_def(fn, attr) \
int get##fn(pid_t pid, char **c) \
{ \
if (pid <= 0) { \
errno = EINVAL; \
return -1; \
} else { \
return getprocattrcon(c, pid, #attr); \
} \
}
...
...
getpidattr_def(pidcon, current)
"getpidattr_def(pidcon, current)" is expanded to getpidcon() function
definition and it calls getprocatrcon()
getprocattrcon() is like this:
static int getprocattrcon(char ** context,
pid_t pid, const char *attr)
{
char *buf;
size_t size;
int fd;
ssize_t ret;
int errno_hold;
fd = openattr(pid, attr, O_RDONLY);
if (fd < 0)
return -1;
size = selinux_page_size;
buf = malloc(size);
if (!buf) {
ret = -1;
goto out;
}
memset(buf, 0, size);
do {
ret = read(fd, buf, size - 1);
} while (ret < 0 && errno == EINTR);
if (ret < 0)
goto out2;
if (ret == 0) {
*context = NULL;
goto out2;
}
*context = strdup(buf);
if (!(*context)) {
ret = -1;
goto out2;
}
ret = 0;
out2:
free(buf);
out:
errno_hold = errno;
close(fd);
errno = errno_hold;
return ret;
}
Pretty simple huh? Just opening some files and reading the contents
and return it by function argument.
It fails at openattr(). I've confirmed this by inserting some log function in
openattr(). openattr() is also simple function.
static int openattr(pid_t pid, const char *attr, int flags)
{
int fd, rc;
char *path;
pid_t tid;
if (pid > 0) {
rc = asprintf(&path, "/proc/%d/attr/%s", pid, attr);
} else if (pid == 0) {
rc = asprintf(&path, "/proc/thread-self/attr/%s", attr);
if (rc < 0)
return -1;
fd = open(path, flags | O_CLOEXEC);
if (fd >= 0 || errno != ENOENT)
goto out;
free(path);
tid = gettid();
rc = asprintf(&path, "/proc/self/task/%d/attr/%s", tid, attr);
} else {
errno = EINVAL;
return -1;
}
if (rc < 0)
return -1;
fd = open(path, flags | O_CLOEXEC);
out:
free(path);
return fd;
}
The fail point is "fd = open(path, flags | O_CLOEXEC);"
Even if the file exists, almost always opening fails. I don't understand ths and want to know what caused the problem. I've confirmed the failure
by inserting some log printing codes, checking android log(adb logcat) and reading the file from android shell(adb shell), e.g. 'cat /proc/412/attr/current'. Reading by 'cat ...' succeeded but log shows the opening the
file fails. The odd thing is if 'pid' is 0, it succeeds.
If opening fails, services can't be launched so the system don't
boot properly. If I ignore the fails and return success from getpidcon()
the system boots properly but this is not the right thing to do obviously.
I'm testing the bsp as selinux permissive mode.
Can anyone have a experience like me? If anyone, please share the
experience and the solution of the problem.
Thank you.
Sangyong Lee.
I want to recieve text data from remote android bluetooth. I had written simple winApi server, but it doesnt respond to remote android sending. The connection was fine and did responds( paired) using control panel manager with my bluetooth. When sending and recieving over control panel manager was working fine. But This time, I want to test using some c++ winapi. Here's my code so far. It was adapted from somewhere codeproject site :)
#include <stdio.h>
#include <stdlib.h>
#include <sstream>
#include <string>
#include <tchar.h>
#include <WinSock2.h>
#include <ws2bth.h>
#include <BluetoothAPIs.h>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
WORD wVersionRequested = 0x202;
WSADATA m_data;
if (0 == WSAStartup(wVersionRequested, &m_data))
{
SOCKET s = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
const DWORD lastError = GetLastError();
if (s == INVALID_SOCKET)
{
ostringstream stream;
stream << lastError;
string data= stream.str();
printf("Failed to get bluetooth socket! %s\n", data.c_str());
return 1;
}
WSAPROTOCOL_INFO protocolInfo;
int protocolInfoSize = sizeof(protocolInfo);
if (0 != getsockopt(s, SOL_SOCKET, SO_PROTOCOL_INFO,
(char*)&protocolInfo, &protocolInfoSize))
{
return 1;
}
SOCKADDR_BTH address;
address.addressFamily = AF_BTH;
address.btAddr = 0;
address.serviceClassId = GUID_NULL;
address.port = BT_PORT_ANY;
sockaddr *pAddr = (sockaddr*)&address;
if (0 != bind(s, pAddr, sizeof(SOCKADDR_BTH)))
{
ostringstream stream;
stream << GetLastError();
string data= stream.str();
printf("%s\n", data.c_str() );
}
else
{
printf("\nBinding Successful....\n");
int length = sizeof(SOCKADDR_BTH) ;
getsockname(s,(sockaddr*)&address,&length);
wprintf (L"Local Bluetooth device is %04x%08x \nServer channel = %d\n",
GET_NAP(address.btAddr), GET_SAP(address.btAddr), address.port);
}
int size = sizeof(SOCKADDR_BTH);
if (0 != getsockname(s, pAddr, &size))
{
ostringstream stream;
stream << GetLastError();
string data= stream.str();
printf("%s\n", data.c_str());
}
if (0 != listen(s, 10))
{
ostringstream stream;
stream << GetLastError();
string data= stream.str();
printf("%s\n", data.c_str());
}
WSAQUERYSET service;
memset(&service, 0, sizeof(service));
service.dwSize = sizeof(service);
// service.lpszServiceInstanceName = reinterpret_cast<LPWSTR>(_T("Accelerometer Data..."));
//service.lpszServiceInstanceName = ;
// service.lpszComment = reinterpret_cast<LPWSTR>(_T("Pushing data to PC"));
GUID serviceID = OBEXFileTransferServiceClass_UUID;
service.lpServiceClassId = &serviceID;
service.dwNumberOfCsAddrs = 1;
service.dwNameSpace = NS_BTH;
CSADDR_INFO csAddr;
memset(&csAddr, 0, sizeof(csAddr));
csAddr.LocalAddr.iSockaddrLength = sizeof(SOCKADDR_BTH);
csAddr.LocalAddr.lpSockaddr = pAddr;
csAddr.iSocketType = SOCK_STREAM;
csAddr.iProtocol = BTHPROTO_RFCOMM;
service.lpcsaBuffer = &csAddr;
if (0 != WSASetService(&service, RNRSERVICE_REGISTER, 0))
{
printf("Service registration failed....");
ostringstream stream;
stream << GetLastError();
string data= stream.str();
printf("%d\n", data.c_str());
}
else
{
printf("\nService registration Successful....\n");
}
HANDLE hRadio ;
BLUETOOTH_FIND_RADIO_PARAMS btfrp = { sizeof(BLUETOOTH_FIND_RADIO_PARAMS) };
HBLUETOOTH_RADIO_FIND hFind = BluetoothFindFirstRadio(&btfrp, &hRadio);
if ((hFind != NULL)&& (hRadio !=NULL))
{
if (BluetoothEnableDiscovery(hRadio,TRUE)){
printf("BluetoothEnableDiscovery() is working!\n");
}
if (BluetoothEnableIncomingConnections(hRadio,TRUE)){
printf("BluetoothEnableIncomingConnections() is working!\n");
}
if (BluetoothIsConnectable(hRadio)){
printf("BluetoothIsConnectable() is working!\n");
}
}
printf("\nBefore accept.........");
SOCKADDR_BTH sab2;
int ilen = sizeof(sab2);
SOCKET s2 = accept (s,(sockaddr*)&sab2, &ilen);
if (s2 == INVALID_SOCKET)
{
wprintf (L"Socket bind, error %d\n", WSAGetLastError ());
}
wprintf (L"\nConnection came from %04x%08x to channel %d\n",
GET_NAP(sab2.btAddr), GET_SAP(sab2.btAddr), sab2.port);
wprintf (L"\nAfter Accept\n");
char buffer[1024] = {0};
memset(buffer, 0, sizeof(buffer));
int r = recv(s2,(char*)buffer, sizeof(buffer), 0);
printf("%s\n",buffer);
closesocket(s2);
if (0 != WSASetService(&service, RNRSERVICE_DELETE, 0))
{
ostringstream stream;
stream << GetLastError();
string data= stream.str();
printf("%s\n", data.c_str());
}
closesocket(s);
WSACleanup();
return 0;
}
}
I use microsoft BT device btw..
Just to be sure: you said you're on WinXP... OK, but WinXP SP3 with Microsoft BT stack and appropriate dongle? or another XP version and another BT stack (like Widcomm or BlueSoleil...)?
In the later case, BT Socket API won't work...
Otherwise, not much to say about the code, except a missing BluetoothFindRadioClose...
I have Tire Pressure Management System (TPMS) adapter that plugs into USB (http://store.mp3car.com/USB_TPMS_Version_2_20_4_Sensor_Kit_p/com-090.htm). I have it working with the original Windows software, as well as Linux C code to read the tire pressures and temperature. I'm now trying to use this adapter on Android and am having some difficulty. I can detect the device fine, but my reads are all returning -1 bytes read, whatever I try. Here's the C code I'm trying to convert:
int TpmsPlugin::readUsbSensor(int sid, unsigned char *buf)
{
int r, transferred;
buf[0] = 0x20 + sid;
r = libusb_interrupt_transfer(mDeviceHandle, ENDPOINT_OUT, buf, 1, &transferred, INTR_TIMEOUT);
if (r < 0) {
DebugOut() << "TPMS: USB write interrupt failed, code " << r << endl;
}
r = libusb_interrupt_transfer(mDeviceHandle, ENDPOINT_IN, buf, 4, &transferred, INTR_TIMEOUT);
if (r < 0) {
DebugOut() << "TPMS: USB read interrupt failed, code " << r << endl;
}
return r;
The value of sid is 1, 2, 3 or 4 depending on the wheel. The values are then extracted with:
lfPressure = ((float)buf[0]-40) * PRESSURE_SCALE * KPA_MULTIPLIER;
lfTemperature = (float)buf[1]-40;
You can see full implementation of this driver here as well: https://github.com/otcshare/automotive-message-broker/blob/master/plugins/tpms/tpmsplugin.cpp
My Android version is able to find the USB device, get permission to use it, connect to it, get the UsbEndpoints (it lists two), but whether bulkTransfer() or controlTransfer() I try, I've failed. In particular, I've tried a lot of different controlTransfer values based on all the docs I could find. Here is some code that I've tried:
UsbInterface intf = TpmsSectionFragment.device.getInterface(0);
UsbEndpoint endpoint_in = null, endpoint_out = null;
for (int i = 0; i < intf.getEndpointCount(); i++) {
UsbEndpoint ep = intf.getEndpoint(i);
if (ep.getDirection() == UsbConstants.USB_DIR_IN)
endpoint_in = ep;
else if (ep.getDirection() == UsbConstants.USB_DIR_OUT)
endpoint_out = ep;
}
UsbDeviceConnection connection = gUsbManager.openDevice(TpmsSectionFragment.device);
connection.claimInterface(intf, false);
int timeout = 1000;
int length = 4;
while (true) {
for (int sensorId = 1; sensorId <= 4 && mReadThreadActive; sensorId++) {
byte[] tpmsRaw = new byte[length];
tpmsRaw[0] = (byte) (0x20 + sensorId);
int out_len = connection.bulkTransfer(endpoint_out, tpmsRaw, 1, timeout);
int in_len = connection.bulkTransfer(endpoint_in, tpmsRaw, 4, timeout);
//int out_len = connection.controlTransfer(0x42, 0x0, 0x100, 0, tpmsRaw, tpmsRaw.length, timeout);
//int in_len = connection.controlTransfer(0x41, 0x0, 0x100, 0, tpmsRaw, tpmsRaw.length, timeout);
Any thoughts on what I could be doing wrong are greatly appreciated. I'm happy to try a few different things to debug further if you have any suggestions.
Thanks!
Here's the solution based on the help from Chris. I converted the calls to queue / requestWait:
ByteBuffer buf = ByteBuffer.allocate(4);
buf.put(0, (byte) (0x20 + sensorId));
UsbRequest send = new UsbRequest();
send.initialize(connection, endpoint_out);
Boolean sent = send.queue(buf, 1);
UsbRequest r1 = connection.requestWait();
send.initialize(connection, endpoint_in);
send.queue(buf, 4);
UsbRequest r2 = connection.requestWait();
The other thing I needed to tweak was this call and set the second parameter to true:
connection.claimInterface(intf, true);
That's it. Done. Thanks for the help!
I am creating an archive on Android using the code like this:
OutputStream os = new FileOutputStream(zipFile);
ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(os));
try
{
zos.setLevel(8);
byte[] buffer = new byte[32768];
for (VFile src : toPack)
{
ZipEntry entry = new ZipEntry(src.name);
zos.putNextEntry(entry);
src.pushToStream(zos, buffer);
src.close();
zos.closeEntry();
}
}
finally
{
zos.close();
}
I found that there's only one compression method available - DEFLATED (there's only STORED alternative available). This means that archive is always compressed with one method.
If I run this code on Android 2.3.4 - I can decompress files in Windows using 7Zip; if I run this on Android 3 (or Samsung Galaxy Tab; not sure who makes it wrong) - 7Zip shows archive list, but cannot decompress file saying Unsupported compression method. At the same time 7Zip shows Deflate as a compression method on file which means it can treat it properly.
Did anyone has this problem?
Thanks.
UDP: Found another topic with similar issue (may be not same though).
The #user1269737's answer is correct; almost. But it only works for a single-file archives.
Below is a code which parses the whole archive.
/**
* Replace wrong local file header byte
* http://sourceforge.net/tracker/?func=detail&aid=3477810&group_id=14481&atid=114481
* Applies to Android API 9-13
* #param zip file
* #throws IOException
*/
private static void fixInvalidZipFile(File zip) throws IOException
{
RandomAccessFile r = new RandomAccessFile(zip, "rw");
try
{
long eocd_offset = findEOCDRecord(r);
if (eocd_offset > 0)
{
r.seek(eocd_offset + 16); // offset of first CDE in EOCD
long cde_offset = readInt(r); // read offset of first Central Directory Entry
long lfh_offset = 0;
long fskip, dskip;
while (true)
{
r.seek(cde_offset);
if (readInt(r) != CDE_SIGNATURE) // got off sync!
return;
r.seek(cde_offset + 20); // compressed file size offset
fskip = readInt(r);
// fix the header
//
r.seek(lfh_offset + 7);
short localFlagsHi = r.readByte(); // hi-order byte of local header flags (general purpose)
r.seek(cde_offset + 9);
short realFlagsHi = r.readByte(); // hi-order byte of central directory flags (general purpose)
if (localFlagsHi != realFlagsHi)
{ // in latest versions this bug is fixed, so we're checking is bug exists.
r.seek(lfh_offset + 7);
r.write(realFlagsHi);
}
// calculate offset of next Central Directory Entry
//
r.seek(cde_offset + 28); // offset of variable CDE parts length in CDE
dskip = 46; // length of fixed CDE part
dskip += readShort(r); // file name
dskip += readShort(r); // extra field
dskip += readShort(r); // file comment
cde_offset += dskip;
if (cde_offset >= eocd_offset) // finished!
break;
// calculate offset of next Local File Header
//
r.seek(lfh_offset + 26); // offset of variable LFH parts length in LFH
fskip += readShort(r); // file name
fskip += readShort(r); // extra field
fskip += 30; // length of fixed LFH part
fskip += 16; // length of Data Descriptor (written after file data)
lfh_offset += fskip;
}
}
}
finally
{
r.close();
}
}
//http://www.pkware.com/documents/casestudies/APPNOTE.TXT
private static final int LFH_SIGNATURE = 0x04034b50;
private static final int DD_SIGNATURE = 0x08074b50;
private static final int CDE_SIGNATURE = 0x02014b50;
private static final int EOCD_SIGNATURE = 0x06054b50;
/** Find an offset of End Of Central Directory record in file */
private static long findEOCDRecord(RandomAccessFile f) throws IOException
{
long result = f.length() - 22; // 22 is minimal EOCD record length
while (result > 0)
{
f.seek(result);
if (readInt(f) == EOCD_SIGNATURE) return result;
result--;
}
return -1;
}
/** Read a 4-byte integer from file converting endianness. */
private static int readInt(RandomAccessFile f) throws IOException
{
int result = 0;
result |= f.read();
result |= (f.read() << 8);
result |= (f.read() << 16);
result |= (f.read() << 24);
return result;
}
/** Read a 2-byte integer from file converting endianness. */
private static short readShort(RandomAccessFile f) throws IOException
{
short result = 0;
result |= f.read();
result |= (f.read() << 8);
return result;
}
Need to be updated. It fixes single-file-zip. You have to look following sequence in zip file { 0, 0x08, 0x08, 0x08, 0 } and replace it to { 0, 0x08, 0x00, 0x08, 0 }
/**
* Replace wrong byte http://sourceforge.net/tracker/?func=detail&aid=3477810&group_id=14481&atid=114481
* #param zip file
* #throws IOException
*/
private static void replaceWrongZipByte(File zip) throws IOException {
RandomAccessFile r = new RandomAccessFile(zip, "rw");
int flag = Integer.parseInt("00001000", 2); //wrong byte
r.seek(7);
int realFlags = r.read();
if( (realFlags & flag) > 0) { // in latest versions this bug is fixed, so we're checking is bug exists.
r.seek(7);
flag = (~flag & 0xff);
// removing only wrong bit, other bits remains the same.
r.write(realFlags & flag);
}
r.close();
}
Update version :
Following code removes all wrong bytes in ZIP.
KMPMatch.java easy to find in google
public static void replaceWrongBytesInZip(File zip) throws IOException {
byte find[] = new byte[] { 0, 0x08, 0x08, 0x08, 0 };
int index;
while( (index = indexOfBytesInFile(zip,find)) != -1) {
replaceWrongZipByte(zip, index + 2);
}
}
private static int indexOfBytesInFile(File file,byte find[]) throws IOException {
byte fileContent[] = new byte[(int) file.length()];
FileInputStream fin = new FileInputStream(file);
fin.read(fileContent);
fin.close();
return KMPMatch.indexOf(fileContent, find);
}
/**
* Replace wrong byte http://sourceforge.net/tracker/?func=detail&aid=3477810&group_id=14481&atid=114481
* #param zip file
* #throws IOException
*/
private static void replaceWrongZipByte(File zip, int wrongByteIndex) throws IOException {
RandomAccessFile r = new RandomAccessFile(zip, "rw");
int flag = Integer.parseInt("00001000", 2);
r.seek(wrongByteIndex);
int realFlags = r.read();
if( (realFlags & flag) > 0) { // in latest versions this bug is fixed, so we're checking is bug exists.
r.seek(wrongByteIndex);
flag = (~flag & 0xff);
// removing only wrong bit, other bits remains the same.
r.write(realFlags & flag);
}
r.close();
}
When I use CreateProcess to create process adb.exe, It will Block in ReadFile.
void KillAdbProcess()
{
DWORD aProcesses[1024], cbNeeded, cProcesses;
unsigned int i;
if ( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) )
return;
cProcesses = cbNeeded / sizeof(DWORD);
for ( i = 0; i < cProcesses; i++ )
if( aProcesses[i] != 0 ){
bool shouldKill =false;
wchar_t szProcessName[MAX_PATH] = L"<unknown>";
//Get a handle to the process.
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ | PROCESS_TERMINATE,
FALSE, aProcesses[i] );
if (NULL != hProcess )
{
HMODULE hMod;
DWORD cbNeeded;
if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod),
&cbNeeded) )
{
GetModuleFileNameExW( hProcess, hMod, szProcessName,
sizeof(szProcessName)/sizeof(TCHAR));
int len = wcslen(szProcessName);
if(!wcscmp(L"\\adb.exe",szProcessName+len-8)){
shouldKill = true;
}
}
}
if(shouldKill) TerminateProcess(hProcess,0);
CloseHandle( hProcess );
}
}
int testadb(){
KillAdbProcess();
char buff[4096] = {0};
int len = sizeof(buff);
DWORD exitCode = 0;
SECURITY_ATTRIBUTES sa;
ZeroMemory(&sa, sizeof(sa));
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
sa.nLength = sizeof(sa);
HANDLE hOutputReadTmp,hOutputRead,hOutputWrite;
// Create the child output pipe.
if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa,0))
return false;
// Create new output read handle and the input write handles. Set
// the Properties to FALSE. Otherwise, the child inherits the
// properties and, as a result, non-closeable handles to the pipes
// are created.
if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp,
GetCurrentProcess(),
&hOutputRead, // Address of new handle.
0,FALSE, // Make it uninheritable.
DUPLICATE_SAME_ACCESS))
return false;
// Close inheritable copies of the handles you do not want to be
// inherited.
if (!CloseHandle(hOutputReadTmp)) return false;
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
STARTUPINFOW si;
GetStartupInfoW(&si);
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdInput = NULL;
if(buff) {
si.hStdOutput = hOutputWrite;
si.hStdError = hOutputWrite;
} else {
si.hStdOutput = NULL;
si.hStdError = NULL;
}
wchar_t cmdBuf[512] = L"adb.exe start-server";
if( !::CreateProcessW(NULL, cmdBuf, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi) )
{
exitCode = -1;
goto exit;
}
::CloseHandle(hOutputWrite);
hOutputWrite = NULL;
len--; //keep it for string end char.
DWORD dwBytes = 0;
DWORD dwHasRead = 0;
while(::ReadFile(hOutputRead, buff+dwHasRead, len-dwHasRead, &dwBytes, NULL))
{
printf("read byte=%d\n",dwBytes);
if(0 == dwBytes) break;
dwHasRead += dwBytes;
//GetExitCodeProcess(pi.hProcess, &exitCode);
//if(STILL_ACTIVE != exitCode) break;
if(dwHasRead >= len) break;
}
buff[dwHasRead] = 0;
::GetExitCodeProcess(pi.hProcess, &exitCode);
exit:
if(hOutputRead) ::CloseHandle(hOutputRead);
if(hOutputWrite) ::CloseHandle(hOutputWrite);
::CloseHandle(pi.hProcess);
::CloseHandle(pi.hThread);
return 0;
}
If I change code to
while(::ReadFile(hOutputRead, buff+dwHasRead, len-dwHasRead, &dwBytes, NULL))
{
printf("read byte=%d\n",dwBytes);
if(0 == dwBytes) break;
dwHasRead += dwBytes;
GetExitCodeProcess(pi.hProcess, &exitCode);
if(STILL_ACTIVE != exitCode) break;
if(dwHasRead >= len) break;
}
it works, but when I delete printf code, it will block again.
while(::ReadFile(hOutputRead, buff+dwHasRead, len-dwHasRead, &dwBytes, NULL))
{
if(0 == dwBytes) break;
dwHasRead += dwBytes;
GetExitCodeProcess(pi.hProcess, &exitCode);
if(STILL_ACTIVE != exitCode) break;
if(dwHasRead >= len) break;
}
In the code of adb.exe, I see some code like belows:
#if ADB_HOST
int launch_server()
{
#ifdef HAVE_WIN32_PROC
/* we need to start the server in the background */
/* we create a PIPE that will be used to wait for the server's "OK" */
/* message since the pipe handles must be inheritable, we use a */
/* security attribute */
HANDLE pipe_read, pipe_write;
SECURITY_ATTRIBUTES sa;
STARTUPINFO startup;
PROCESS_INFORMATION pinfo;
char program_path[ MAX_PATH ];
int ret;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
/* create pipe, and ensure its read handle isn't inheritable */
ret = CreatePipe( &pipe_read, &pipe_write, &sa, 0 );
if (!ret) {
fprintf(stderr, "CreatePipe() failure, error %ld\n", GetLastError() );
return -1;
}
SetHandleInformation( pipe_read, HANDLE_FLAG_INHERIT, 0 );
ZeroMemory( &startup, sizeof(startup) );
startup.cb = sizeof(startup);
startup.hStdInput = GetStdHandle( STD_INPUT_HANDLE );
startup.hStdOutput = pipe_write;
startup.hStdError = GetStdHandle( STD_ERROR_HANDLE );
startup.dwFlags = STARTF_USESTDHANDLES;
ZeroMemory( &pinfo, sizeof(pinfo) );
/* get path of current program */
GetModuleFileName( NULL, program_path, sizeof(program_path) );
ret = CreateProcess(
program_path, /* program path */
"adb fork-server server",
/* the fork-server argument will set the
debug = 2 in the child */
NULL, /* process handle is not inheritable */
NULL, /* thread handle is not inheritable */
TRUE, /* yes, inherit some handles */
DETACHED_PROCESS, /* the new process doesn't have a console */
NULL, /* use parent's environment block */
NULL, /* use parent's starting directory */
&startup, /* startup info, i.e. std handles */
&pinfo );
CloseHandle( pipe_write );
if (!ret) {
fprintf(stderr, "CreateProcess failure, error %ld\n", GetLastError() );
CloseHandle( pipe_read );
return -1;
}
CloseHandle( pinfo.hProcess );
CloseHandle( pinfo.hThread );
/* wait for the "OK\n" message */
{
char temp[3];
DWORD count;
ret = ReadFile( pipe_read, temp, 3, &count, NULL );
CloseHandle( pipe_read );
if ( !ret ) {
fprintf(stderr, "could not read ok from ADB Server, error = %ld\n", GetLastError() );
return -1;
}
if (count != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
fprintf(stderr, "ADB server didn't ACK\n" );
return -1;
}
}
#elif defined(HAVE_FORKEXEC)
char path[PATH_MAX];
int fd[2];
// set up a pipe so the child can tell us when it is ready.
// fd[0] will be parent's end, and fd[1] will get mapped to stderr in the child.
if (pipe(fd)) {
fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno);
return -1;
}
get_my_path(path);
pid_t pid = fork();
if(pid < 0) return -1;
if (pid == 0) {
// child side of the fork
// redirect stderr to the pipe
// we use stderr instead of stdout due to stdout's buffering behavior.
adb_close(fd[0]);
dup2(fd[1], STDERR_FILENO);
adb_close(fd[1]);
// child process
int result = execl(path, "adb", "fork-server", "server", NULL);
// this should not return
fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
} else {
// parent side of the fork
char temp[3];
temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C';
// wait for the "OK\n" message
adb_close(fd[1]);
int ret = adb_read(fd[0], temp, 3);
adb_close(fd[0]);
if (ret < 0) {
fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", errno);
return -1;
}
if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
fprintf(stderr, "ADB server didn't ACK\n" );
return -1;
}
setsid();
}
#else
#error "cannot implement background server start on this platform"
#endif
return 0;
}
#endif
I think the child process of adb.exe inherit the handle of adb.exe, if the child process of adb.exe doesn't exit, ReadFile will block for ever. But when I exec "adb.exe start-server" in command, all is Ok. So how does windows command call CreateProcess and ReadFile?
I have found the answer: Redirecting an arbitrary Console's Input/Output - CodeProject.
The technique of redirecting the input/output of a console process is very sample: The CreateProcess() API through the STARTUPINFO structure enables us to redirect the standard handles of a child console based process. So we can set these handles to either a pipe handle, file handle, or any handle that we can read and write. The detail of this technique has been described clearly in MSDN: HOWTO: Spawn Console Processes with Redirected Standard Handles.
However, MSDN's sample code has two big problem. First, it assumes the child process will send output at first, then wait for input, then flush the output buffer and exit. If the child process doesn't behave like that, the parent process will be hung up. The reason of this is the ReadFile() function remains blocked untill the child process sends some output, or exits.
Second, It has problem to redirect a 16-bit console (including console based MS-DOS applications.) On Windows 9x, ReadFile remains blocked even after the child process has terminated; On Windows NT/XP, ReadFile always returns FALSE with error code set to ERROR_BROKEN_PIPE if the child process is a DOS application.
Solving the block problem of ReadFile
To prevent the parent process from being blocked by ReadFile, we can simply pass a file handle as stdout to the child process, then monitor this file. A more simple way is to call PeekNamedPipe() function before calling ReadFile(). The PeekNamedPipe function checks information about data in the pipe, then returns immediately. If there's no data available in the pipe, don't call ReadFile.
By calling PeekNamedPipe before ReadFile, we also solve the block problem of redirecting a 16-bit console on Windows 9x.